Pular para o conteúdo

Dialog

templates/components/Dialog

É o único componente que sequestra a atenção da tela inteira: modal sobre o elemento nativo <dialog> com foco preso (focus trap), fundo escurecido/desfocado e camada superior, para forçar uma decisão ou tarefa focada. Por isso seus enriquecedores são quase todos comportamentais/de foco/de a11y, não elementos de barra reposicionáveis.

Base congelada

Edit profile

Make changes to your profile here. Click save when you're done.

Conteúdo

Tamanho (largura)

Escolhe o preset de largura máxima…

Escolhe o preset de largura máxima do diálogo (sm é o padrão da base); mantém cantos, padding, tipografia e anel idênticos — só muda o teto de largura do conteúdo.

Formas de fechar

Habilita/desabilita cada via de fechamento independentemente.…

Habilita/desabilita cada via de fechamento independentemente. Permite tornar o diálogo não-descartável (ex.: formulário obrigatório) sem tocar no visual.

Guarda de alterações não salvas

Intercepta o fechamento (ESC/backdrop/X/Cancelar) quando um…

Intercepta o fechamento (ESC/backdrop/X/Cancelar) quando um campo do formulário interno foi alterado e pede confirmação antes de descartar. Feito para diálogos de edição como o de perfil.

Modo alerta (alertdialog)

Promove o diálogo a role=alertdialog: torna-o…

Promove o diálogo a role=alertdialog: torna-o não-descartável por ESC/backdrop e exige uma decisão explícita (confirmar/cancelar). Camada de a11y e comportamento, sem mudança visual.

Foco inicial

Define qual elemento recebe o foco…

Define qual elemento recebe o foco ao abrir, dentro do focus trap já existente na base — útil para levar direto ao primeiro campo ou à ação principal.

Ícone no cabeçalho

Adiciona um glifo semântico (monocromático, herdando…

Adiciona um glifo semântico (monocromático, herdando a cor da base) antes do título, para reforçar a intenção em diálogos de alerta/confirmação. Conteúdo aditivo dentro do gap já existente do Header.

Comportamento

Corpo rolável (scroll interno)

Quando o conteúdo excede a altura…

Quando o conteúdo excede a altura da viewport, fixa Header e Footer e cria uma região central com rolagem vertical, em vez de o diálogo estourar a tela. Puramente comportamental (overflow).

Abrir por URL (deep-link)

Faz o diálogo abrir quando o…

Faz o diálogo abrir quando o hash da URL casa com seu id e registra no histórico, tornando-o compartilhável por link e fechável pelo botão Voltar do navegador. Só comportamento de abertura.

Bloqueio ao processar (busy)

Enquanto a ação de confirmar roda…

Enquanto a ação de confirmar roda de forma assíncrona, trava o diálogo: desabilita botões e vias de fechar e impede duplo envio até a conclusão. Comportamental, sem alterar estilo.
Código gerado

Cole no Claude Code — ele acerta de primeira.

Dialog (UX) — Design System (Symfony UX Toolkit / shadcn)

BASE CONGELADA (não mude por instância): O visual de popover (rounded-xl, bg-popover, ring-1 ring-foreground/10, p-4, centralizado fixed) e o preset padrão max-w-sm; a animação de abrir/fechar (scale-95→100 + opacity + backdrop-blur/black/10); a tipografia do Título (cn-font-heading text-base leading-none font-medium) e da Descrição (text-muted-foreground text-sm); o Rodapé (bg-muted/50, border-t, -mx-4 -mb-4, sm:justify-end); o botão X (Button ghost icon-sm no canto superior); e o wiring de a11y (elemento dialog nativo, aria-labelledby/aria-describedby, sr-only "Close", suporte rtl/ltr). Nada disso muda ao ligar/desligar capacidade.

USO: <twig:Dialog>…</twig:Dialog>

CAPACIDADES OPT-IN (ligue sem alterar o visual):
- Tamanho (largura): Escolhe o preset de largura máxima do diálogo (sm é o padrão da base); mantém cantos, padding, tipografia e anel idênticos — só muda o teto de largura do conteúdo. [select → Pequeno (sm) / Médio (md) / Grande (lg) / Extra grande (xl) / Tela cheia]- Formas de fechar: Habilita/desabilita cada via de fechamento independentemente. Permite tornar o diálogo não-descartável (ex.: formulário obrigatório) sem tocar no visual. [multi → Tecla ESC / Clique no fundo (backdrop) / Botão X (canto)]- Corpo rolável (scroll interno): Quando o conteúdo excede a altura da viewport, fixa Header e Footer e cria uma região central com rolagem vertical, em vez de o diálogo estourar a tela. Puramente comportamental (overflow). [toggle]- Guarda de alterações não salvas: Intercepta o fechamento (ESC/backdrop/X/Cancelar) quando um campo do formulário interno foi alterado e pede confirmação antes de descartar. Feito para diálogos de edição como o de perfil. [toggle]- Modo alerta (alertdialog): Promove o diálogo a role=alertdialog: torna-o não-descartável por ESC/backdrop e exige uma decisão explícita (confirmar/cancelar). Camada de a11y e comportamento, sem mudança visual. [toggle]- Foco inicial: Define qual elemento recebe o foco ao abrir, dentro do focus trap já existente na base — útil para levar direto ao primeiro campo ou à ação principal. [select → Primeiro campo do formulário / Botão de ação principal / O próprio diálogo (nenhum campo)]- Abrir por URL (deep-link): Faz o diálogo abrir quando o hash da URL casa com seu id e registra no histórico, tornando-o compartilhável por link e fechável pelo botão Voltar do navegador. Só comportamento de abertura. [toggle]- Bloqueio ao processar (busy): Enquanto a ação de confirmar roda de forma assíncrona, trava o diálogo: desabilita botões e vias de fechar e impede duplo envio até a conclusão. Comportamental, sem alterar estilo. [toggle]- Ícone no cabeçalho: Adiciona um glifo semântico (monocromático, herdando a cor da base) antes do título, para reforçar a intenção em diálogos de alerta/confirmação. Conteúdo aditivo dentro do gap já existente do Header. [select → Nenhum / Informação / Aviso / Erro / Sucesso]FAÇA:
- Use <twig:Nome> — a base é congelada, você é dono do template em templates/components/.
- Ligue apenas capacidades opt-in listadas (e_funcao=true); a aparência não muda ao ligá-las.
- Passe atributos extras (id, aria-*, name, data-*) via {{ attributes }}.
NÃO FAÇA:
- Não mude cor/fonte/cantos/espaçamento por instância — é decisão de BASE, na fonte única assets/styles/app.css.
- Não crie variante/fork para a mesma coisa — existe UMA base por componente.
- Não reimplemente o componente nem adicione toolchain Node.
TESTE ANTES DE MUDAR: "é função ou é base?" — função = capacidade opt-in; base = mude o token na fonte única (assets/styles/app.css), para todos.

A11Y (herdada da base): e o wiring de a11y (elemento dialog nativo, aria-labelledby/aria-describedby, sr-only "Close", suporte rtl/ltr)

Via MCP: tool get_component com {"id": "dialog"} · list_capabilities("dialog").

Spec crua: config/ds-specs/dialog.json · Conectar o MCP.

Spec machine-readable (JSON)

{
    "$schema_version": "1.0",
    "id": "dialog",
    "component": "Dialog",
    "eixo": "ux",
    "particularidade": "É o único componente que sequestra a atenção da tela inteira: modal sobre o elemento nativo &lt;dialog&gt; com foco preso (focus trap), fundo escurecido/desfocado e camada superior, para forçar uma decisão ou tarefa focada. Por isso seus enriquecedores são quase todos comportamentais/de foco/de a11y, não elementos de barra reposicionáveis.",
    "base_congelada": "O visual de popover (rounded-xl, bg-popover, ring-1 ring-foreground/10, p-4, centralizado fixed) e o preset padrão max-w-sm; a animação de abrir/fechar (scale-95→100 + opacity + backdrop-blur/black/10); a tipografia do Título (cn-font-heading text-base leading-none font-medium) e da Descrição (text-muted-foreground text-sm); o Rodapé (bg-muted/50, border-t, -mx-4 -mb-4, sm:justify-end); o botão X (Button ghost icon-sm no canto superior); e o wiring de a11y (elemento dialog nativo, aria-labelledby/aria-describedby, sr-only \"Close\", suporte rtl/ltr). Nada disso muda ao ligar/desligar capacidade.",
    "props": [
        {
            "name": "open",
            "type": "boolean",
            "default": "false",
            "description": "Whether the dialog is open on initial render. Defaults to `false` #}"
        },
        {
            "name": "id",
            "type": "string",
            "default": null,
            "description": "Unique identifier used to generate internal Dialog IDs #}"
        }
    ],
    "capacidades": [
        {
            "id": "tamanho-largura",
            "nome": "Tamanho (largura)",
            "descricao": "Escolhe o preset de largura máxima do diálogo (sm é o padrão da base); mantém cantos, padding, tipografia e anel idênticos — só muda o teto de largura do conteúdo.",
            "controle": "select",
            "opcoes": [
                "Pequeno (sm)",
                "Médio (md)",
                "Grande (lg)",
                "Extra grande (xl)",
                "Tela cheia"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ux/dialog; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "formas-de-fechar",
            "nome": "Formas de fechar",
            "descricao": "Habilita/desabilita cada via de fechamento independentemente. Permite tornar o diálogo não-descartável (ex.: formulário obrigatório) sem tocar no visual.",
            "controle": "multi",
            "opcoes": [
                "Tecla ESC",
                "Clique no fundo (backdrop)",
                "Botão X (canto)"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (multi). Ligue no configurador em /vitrine/ux/dialog; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "corpo-rol-vel-scroll-interno",
            "nome": "Corpo rolável (scroll interno)",
            "descricao": "Quando o conteúdo excede a altura da viewport, fixa Header e Footer e cria uma região central com rolagem vertical, em vez de o diálogo estourar a tela. Puramente comportamental (overflow).",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/dialog; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "guarda-de-altera-es-n-o-salvas",
            "nome": "Guarda de alterações não salvas",
            "descricao": "Intercepta o fechamento (ESC/backdrop/X/Cancelar) quando um campo do formulário interno foi alterado e pede confirmação antes de descartar. Feito para diálogos de edição como o de perfil.",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/dialog; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "modo-alerta-alertdialog",
            "nome": "Modo alerta (alertdialog)",
            "descricao": "Promove o diálogo a role=alertdialog: torna-o não-descartável por ESC/backdrop e exige uma decisão explícita (confirmar/cancelar). Camada de a11y e comportamento, sem mudança visual.",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/dialog; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "foco-inicial",
            "nome": "Foco inicial",
            "descricao": "Define qual elemento recebe o foco ao abrir, dentro do focus trap já existente na base — útil para levar direto ao primeiro campo ou à ação principal.",
            "controle": "select",
            "opcoes": [
                "Primeiro campo do formulário",
                "Botão de ação principal",
                "O próprio diálogo (nenhum campo)"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ux/dialog; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "abrir-por-url-deep-link",
            "nome": "Abrir por URL (deep-link)",
            "descricao": "Faz o diálogo abrir quando o hash da URL casa com seu id e registra no histórico, tornando-o compartilhável por link e fechável pelo botão Voltar do navegador. Só comportamento de abertura.",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/dialog; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "bloqueio-ao-processar-busy",
            "nome": "Bloqueio ao processar (busy)",
            "descricao": "Enquanto a ação de confirmar roda de forma assíncrona, trava o diálogo: desabilita botões e vias de fechar e impede duplo envio até a conclusão. Comportamental, sem alterar estilo.",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/dialog; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "cone-no-cabe-alho",
            "nome": "Ícone no cabeçalho",
            "descricao": "Adiciona um glifo semântico (monocromático, herdando a cor da base) antes do título, para reforçar a intenção em diálogos de alerta/confirmação. Conteúdo aditivo dentro do gap já existente do Header.",
            "controle": "select",
            "opcoes": [
                "Nenhum",
                "Informação",
                "Aviso",
                "Erro",
                "Sucesso"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ux/dialog; comportamento via controller Stimulus. Não altera a base."
        }
    ],
    "snippet_uso": "<twig:Dialog>…</twig:Dialog>",
    "exemplo_demo": "<twig:Dialog id=\"edit_profile\">\n    <twig:Dialog:Trigger>\n        <twig:Button variant=\"outline\" {{ ...dialog_trigger_attrs }}>Open Dialog</twig:Button>\n    </twig:Dialog:Trigger>\n    <twig:Dialog:Content>\n        <twig:Dialog:Header>\n            <twig:Dialog:Title>Edit profile</twig:Dialog:Title>\n            <twig:Dialog:Description>\n                Make changes to your profile here. Click save when you&apos;re done.\n            </twig:Dialog:Description>\n        </twig:Dialog:Header>\n        <div class=\"grid gap-4\">\n            <div class=\"grid gap-3\">\n                <twig:Label for=\"name\">Name</twig:Label>\n                <twig:Input id=\"name\" name=\"name\" value=\"Pedro Duarte\" />\n            </div>\n            <div class=\"grid gap-3\">\n                <twig:Label for=\"username\">Username</twig:Label>\n                <twig:Input id=\"username\" name=\"username\" value=\"@peduarte\" />\n            </div>\n        </div>\n        <twig:Dialog:Footer>\n            <twig:Dialog:Close>\n                <twig:Button variant=\"outline\" {{ ...dialog_close_attrs }}>Cancel</twig:Button>\n            </twig:Dialog:Close>\n            <twig:Button type=\"submit\">Save changes</twig:Button>\n        </twig:Dialog:Footer>\n    </twig:Dialog:Content>\n</twig:Dialog>",
    "regras": {
        "faca": [
            "Use <twig:Nome> — a base é congelada, você é dono do template em templates/components/.",
            "Ligue apenas capacidades opt-in listadas (e_funcao=true); a aparência não muda ao ligá-las.",
            "Passe atributos extras (id, aria-*, name, data-*) via {{ attributes }}."
        ],
        "nao_faca": [
            "Não mude cor/fonte/cantos/espaçamento por instância — é decisão de BASE, na fonte única assets/styles/app.css.",
            "Não crie variante/fork para a mesma coisa — existe UMA base por componente.",
            "Não reimplemente o componente nem adicione toolchain Node."
        ]
    },
    "a11y": "e o wiring de a11y (elemento dialog nativo, aria-labelledby/aria-describedby, sr-only \"Close\", suporte rtl/ltr)",
    "mcp": {
        "tool": "get_component",
        "args": {
            "id": "dialog"
        }
    }
}