Accordion
templates/components/Accordion
O Accordion é uma pilha de seções colapsáveis: sua particularidade é gerenciar QUAIS painéis estão revelados — abrir um ou vários, navegar por teclado e animar a expansão de cada bloco de conteúdo sob cabeçalhos clicáveis. Os enrichers giram em torno de controlar, encontrar, lembrar e anotar essas seções, sem tocar no visual congelado do disclosure.
Base congelada
Capacidades 8
Conteúdo
Lembrar seções abertas
Persiste quais paineis o usuario deixou…
Carregar conteúdo sob demanda
Adia a renderizacao do conteudo de…
Contador no cabeçalho
Slot de contagem numerica (Badge congelado)…
Ícone da seção
Slot de icone por item ao…
Comportamento
Expandir / recolher todos
Botão de barra que abre ou…
Buscar e filtrar seções
Campo de busca que filtra os…
Rolar até a seção ao abrir
Ao expandir um item, rola a…
Manter ao menos um aberto
Impede recolher a ultima secao aberta…
Claude Code
Cole no Claude Code — ele acerta de primeira.
Accordion (UX) — Design System (Symfony UX Toolkit / shadcn)
BASE CONGELADA (não mude por instância): Estrutura Trigger+Content e o chevron para baixo/cima que alterna sozinho à direita; a borda entre itens (not-last:border-b), cantos rounded-lg, espaçamento py-2.5, texto text-sm font-medium, a animação grid-template-rows (duration-300 ease-out) e toda a a11y: h3>button, aria-expanded/aria-controls, role=region, aria-labelledby, navegação por setas (up/down conforme orientation) e anel de foco. Os props NATIVOS multiple (um vs. vários abertos), defaultValue (quais abrem), orientation (vertical/horizontal) e o disabled/open por Item já são BASE — não contam como enrichers.
USO: <twig:Accordion />
CAPACIDADES OPT-IN (ligue sem alterar o visual):
- Expandir / recolher todos: Botão de barra que abre ou fecha todas as seções de uma vez; só faz sentido quando a base está em multiple=true. Reflete o estado atual (Expandir tudo x Recolher tudo). [toggle]- Buscar e filtrar seções: Campo de busca que filtra os itens pelo texto do cabeçalho (e opcionalmente do conteudo), ocultando os que nao casam e auto-expandindo os que casam. Ideal para FAQ longa. [toggle]- Lembrar seções abertas: Persiste quais paineis o usuario deixou abertos e restaura no proximo acesso, ou permite deep-link para uma secao especifica. Comportamento puro sobre o defaultValue da base. [select → Nao lembrar / Local (localStorage) / URL (deep-link no hash)]- Rolar até a seção ao abrir: Ao expandir um item, rola a pagina para trazer o cabecalho/conteudo a vista — util quando o painel aberto cresce e empurra o resto para fora da tela. [toggle]- Carregar conteúdo sob demanda: Adia a renderizacao do conteudo de cada painel ate a primeira abertura (lazy). Alivia paginas com muitas secoes pesadas; a base continua identica, so muda quando o HTML entra. [toggle]- Manter ao menos um aberto: Impede recolher a ultima secao aberta (nao-colapsavel), garantindo que sempre haja um painel visivel. Guarda de comportamento independente do multiple. [toggle]- Contador no cabeçalho: Slot de contagem numerica (Badge congelado) por secao — ex.: no de itens/pendencias dentro do painel — exibido na linha do Trigger. Conteudo aditivo, a la badge por aba. [toggle]- Ícone da seção: Slot de icone por item ao lado do rotulo do Trigger, para categorizar visualmente as secoes (financeiro, seguranca, envio...). Conteudo aditivo usando o tamanho de icone ja congelado. [toggle]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): 5, texto text-sm font-medium, a animação grid-template-rows (duration-300 ease-out) e toda a a11y: h3>button, aria-expanded/aria-controls, role=region, aria-labelledby, navegação por setas (up/down conforme orientation) e anel de foco
LLM / MCP
Via MCP: tool get_component com {"id": "accordion"} · list_capabilities("accordion").
Spec crua: config/ds-specs/accordion.json · Conectar o MCP.
Spec machine-readable (JSON)
{
"$schema_version": "1.0",
"id": "accordion",
"component": "Accordion",
"eixo": "ux",
"particularidade": "O Accordion é uma pilha de seções colapsáveis: sua particularidade é gerenciar QUAIS painéis estão revelados — abrir um ou vários, navegar por teclado e animar a expansão de cada bloco de conteúdo sob cabeçalhos clicáveis. Os enrichers giram em torno de controlar, encontrar, lembrar e anotar essas seções, sem tocar no visual congelado do disclosure.",
"base_congelada": "Estrutura Trigger+Content e o chevron para baixo/cima que alterna sozinho à direita; a borda entre itens (not-last:border-b), cantos rounded-lg, espaçamento py-2.5, texto text-sm font-medium, a animação grid-template-rows (duration-300 ease-out) e toda a a11y: h3>button, aria-expanded/aria-controls, role=region, aria-labelledby, navegação por setas (up/down conforme orientation) e anel de foco. Os props NATIVOS multiple (um vs. vários abertos), defaultValue (quais abrem), orientation (vertical/horizontal) e o disabled/open por Item já são BASE — não contam como enrichers.",
"props": [
{
"name": "multiple",
"type": "boolean",
"default": "false",
"description": "Whether multiple items can be opened at once. Defaults to `false` #}"
},
{
"name": "defaultValue",
"type": "string|array<string>|null",
"default": "null",
"description": "Value(s) of the item(s) to open by default #}"
},
{
"name": "orientation",
"type": "'vertical'|'horizontal'",
"default": "vertical",
"description": "The visual orientation, controls whether keyboard navigation uses up/down or left/right arrow keys. Defaults to `vertical` #}"
},
{
"name": "id",
"type": "string",
"default": null,
"description": "Unique identifier for the Accordion #}"
}
],
"capacidades": [
{
"id": "expandir-recolher-todos",
"nome": "Expandir / recolher todos",
"descricao": "Botão de barra que abre ou fecha todas as seções de uma vez; só faz sentido quando a base está em multiple=true. Reflete o estado atual (Expandir tudo x Recolher tudo).",
"controle": "toggle",
"opcoes": [],
"posicionavel": true,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/accordion; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "buscar-e-filtrar-se-es",
"nome": "Buscar e filtrar seções",
"descricao": "Campo de busca que filtra os itens pelo texto do cabeçalho (e opcionalmente do conteudo), ocultando os que nao casam e auto-expandindo os que casam. Ideal para FAQ longa.",
"controle": "toggle",
"opcoes": [],
"posicionavel": true,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/accordion; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "lembrar-se-es-abertas",
"nome": "Lembrar seções abertas",
"descricao": "Persiste quais paineis o usuario deixou abertos e restaura no proximo acesso, ou permite deep-link para uma secao especifica. Comportamento puro sobre o defaultValue da base.",
"controle": "select",
"opcoes": [
"Nao lembrar",
"Local (localStorage)",
"URL (deep-link no hash)"
],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ux/accordion; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "rolar-at-a-se-o-ao-abrir",
"nome": "Rolar até a seção ao abrir",
"descricao": "Ao expandir um item, rola a pagina para trazer o cabecalho/conteudo a vista — util quando o painel aberto cresce e empurra o resto para fora da tela.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/accordion; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "carregar-conte-do-sob-demanda",
"nome": "Carregar conteúdo sob demanda",
"descricao": "Adia a renderizacao do conteudo de cada painel ate a primeira abertura (lazy). Alivia paginas com muitas secoes pesadas; a base continua identica, so muda quando o HTML entra.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/accordion; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "manter-ao-menos-um-aberto",
"nome": "Manter ao menos um aberto",
"descricao": "Impede recolher a ultima secao aberta (nao-colapsavel), garantindo que sempre haja um painel visivel. Guarda de comportamento independente do multiple.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/accordion; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "contador-no-cabe-alho",
"nome": "Contador no cabeçalho",
"descricao": "Slot de contagem numerica (Badge congelado) por secao — ex.: no de itens/pendencias dentro do painel — exibido na linha do Trigger. Conteudo aditivo, a la badge por aba.",
"controle": "toggle",
"opcoes": [],
"posicionavel": true,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/accordion; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "cone-da-se-o",
"nome": "Ícone da seção",
"descricao": "Slot de icone por item ao lado do rotulo do Trigger, para categorizar visualmente as secoes (financeiro, seguranca, envio...). Conteudo aditivo usando o tamanho de icone ja congelado.",
"controle": "toggle",
"opcoes": [],
"posicionavel": true,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/accordion; comportamento via controller Stimulus. Não altera a base."
}
],
"snippet_uso": "<twig:Accordion />",
"exemplo_demo": "<twig:Accordion id=\"accordion-demo\" defaultValue=\"{{ ['shipping'] }}\" class=\"max-w-lg\">\n <twig:Accordion:Item value=\"shipping\">\n <twig:Accordion:Trigger>What are your shipping options?</twig:Accordion:Trigger>\n <twig:Accordion:Content>\n We offer standard (5-7 days), express (2-3 days), and overnight\n shipping. Free shipping on international orders.\n </twig:Accordion:Content>\n </twig:Accordion:Item>\n <twig:Accordion:Item value=\"returns\">\n <twig:Accordion:Trigger>What is your return policy?</twig:Accordion:Trigger>\n <twig:Accordion:Content>\n Returns accepted within 30 days. Items must be unused and in original\n packaging. Refunds processed within 5-7 business days.\n </twig:Accordion:Content>\n </twig:Accordion:Item>\n <twig:Accordion:Item value=\"support\">\n <twig:Accordion:Trigger>How can I contact customer support?</twig:Accordion:Trigger>\n <twig:Accordion:Content>\n Reach us via email, live chat, or phone. We respond within 24 hours\n during business days.\n </twig:Accordion:Content>\n </twig:Accordion:Item>\n</twig:Accordion>",
"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": "5, texto text-sm font-medium, a animação grid-template-rows (duration-300 ease-out) e toda a a11y: h3>button, aria-expanded/aria-controls, role=region, aria-labelledby, navegação por setas (up/down conforme orientation) e anel de foco",
"mcp": {
"tool": "get_component",
"args": {
"id": "accordion"
}
}
}