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
Capacidades 9
Conteúdo
Tamanho (largura)
Escolhe o preset de largura máxima…
Formas de fechar
Habilita/desabilita cada via de fechamento independentemente.…
Guarda de alterações não salvas
Intercepta o fechamento (ESC/backdrop/X/Cancelar) quando um…
Modo alerta (alertdialog)
Promove o diálogo a role=alertdialog: torna-o…
Foco inicial
Define qual elemento recebe o foco…
Ícone no cabeçalho
Adiciona um glifo semântico (monocromático, herdando…
Comportamento
Corpo rolável (scroll interno)
Quando o conteúdo excede a altura…
Abrir por URL (deep-link)
Faz o diálogo abrir quando o…
Bloqueio ao processar (busy)
Enquanto a ação de confirmar roda…
Claude Code
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)
LLM / MCP
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 <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": "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'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"
}
}
}