Accordion
Em andamentoLista de seções recolhíveis: cada título é um botão dentro de um cabeçalho que abre e fecha o conteúdo abaixo. Uma aberta por vez, ou várias com allowMultiple, em tema claro ou escuro; pode abrir já num item e ser controlada de fora.
- Função
- Conteúdo
- Também chamado de
- sanfona, acordeão, recolhível, expansível, accordion
- Mora em
- app/components/molecules/Accordion.tsx
- Última mudança
- 2026-09-30
Prévia
A peça ao vivo, vinda da ficha, na largura escolhida. Em larguras fixas, ela roda num quadro do tamanho de uma tela de verdade.
Na largura da página: 0px
Variantes na ficha
Lidas da ficha no build, na ordem do palco. Cada uma abre sozinha, sem o styleguide em volta — é o endereço que o palco carrega.
Quando usar
Para perguntas e conteúdos longos que não precisam aparecer todos de uma vez — módulos de um curso, regras de uma bolsa.
Faça
- Escreva cada título como a pergunta ou o assunto inteiro — é o nome do botão.
- Acerte
nivelDoTitulopela página (padrão 3): é por ele que quem usa leitor pula de item em item pela tecla H. - Use
allowMultiplequando a pessoa precisar comparar dois itens abertos. - Use
tema="escuro"sobre fundo escuro: o anel de foco passa a ser o claro, e o subtítulo, claro (E65). - Abra no máximo um de início, com
defaultOpenIds— a grade no módulo atual (E65). - Use
openIdseonOpenChangepara abrir por fora: um link direto para uma pergunta.
Evite
- Esconder no acordeão a informação que quase todos precisam: o que é essencial fica à vista.
- Vários itens abertos de saída: vira uma página comprida sem o índice que o acordeão prometia.
- Um item só: sem outros, é só um botão de "mostrar mais".
Quando não usar. Para perguntas frequentes com título de seção e botão "ver todas", use o FAQ (que já usa o Accordion). Para a grade curricular, o CurriculumAccordion. Para alternar seções longas, o CourseTabs.
Nota de uso
Teclado (E65): Enter e Espaço abrem e fecham; o Tab entra no painel aberto (decisão local, para o texto revelado entrar no caminho) e segue para o próximo item. Não esconda aqui o que é essencial, e abra no máximo um de início (defaultOpenIds). Para abrir por fora — um link direto para uma pergunta —, openIds e onOpenChange. Ganhou aria-expanded e aria-controls na E08b — antes, quem ouvia não sabia se a seção estava aberta. O conteúdo recolhido também saiu da ordem de tabulação. A prop variant existia na interface e não fazia nada; foi removida. No tema escuro (o padrão do FAQ), o anel de foco é o inverso, claro: o --ring media 2,38:1 ali e sumia. Cada pergunta é um cabeçalho (nivelDoTitulo, padrão 3) com o botão dentro: é por ele que quem usa leitor pula de pergunta em pergunta pela tecla H. Acerte o nível conforme a página.
Anatomia
As partes, na ordem em que o olho — e o teclado — passam.
- Item — Caixa de cantos `rounded-2xl` com borda.
- Cabeçalho — `h2` a `h6` (padrão `h3`), sem estilo próprio.
- Botão — Ocupa a largura: subtítulo opcional (verde no claro, `--ink-inverse-muted` no escuro), título em negrito e a seta num círculo.
- Painel — `role="region"` nomeada pelo botão, com o conteúdo.
Estados
Os estados que a peça tem no código, e o que muda em cada um.
| Estado | O que muda |
|---|---|
| Fechado | aria-expanded="false", painel hidden e fora do Tab. |
| Aberto | aria-expanded="true", painel à mostra, seta girada 180° e o painel recebendo foco (tabIndex={0}). |
| Foco | Anel por dentro do botão: ring-ring no claro, ring-ring-inverse no escuro. |
| Hover | Fundo leve no botão; no claro, a sombra do item aumenta. |
Comportamento
| Situação | O que acontece |
|---|---|
| Abrir um item (padrão) | Fecha o que estava aberto — medido: com o Módulo I aberto, abrir o Módulo II fecha o I. |
| Abrir com `allowMultiple` | Os outros continuam abertos — medido: dois abertos. |
| Clicar de novo no aberto | Fecha — e o painel volta ao hidden, conferido no teste da ficha. |
| Com `defaultOpenIds` | Abre de saída nos itens pedidos (E65). |
| Modo controlado (`openIds`) | Quem manda é o consumidor; onOpenChange recebe a lista nova a cada troca, nos dois modos — conferido no teste da ficha. |
Relacionados
Como usar no código
A importação e o menor uso, lidos do arquivo. O … marca as props obrigatórias.
import Accordion from '@/app/components/molecules/Accordion';
<Accordion items={…}>…</Accordion>Propriedades
Lidas do código da peça no build — não escritas à mão.
| Prop | Tipo | Padrão | Obrigatória | Descrição |
|---|---|---|---|---|
items | { id: IdDoItem; title: string; subtitle?: string; content: React.ReactNode; }[] | — | sim | — |
allowMultiple | boolean | false | não | — |
tema | TemaDoAcordeao | 'claro' | não | — |
nivelDoTitulo | AccordionItemProps['nivelDoTitulo'] | — | não | Nível do cabeçalho de cada item (padrão 3). Ver AccordionItem. |
defaultOpenIds | IdDoItem[] | — | não | Itens abertos de início (E65): a grade curricular aberta no módulo atual. Abra no máximo um — vários abertos de saída é uma página comprida sem o índice que o acordeão prometia. |
openIds | IdDoItem[] | — | não | Presente = modo controlado (E65): um link direto para uma pergunta. |
onOpenChange | (ids: IdDoItem[]) => void | — | não | Dispara a cada abertura e fechamento, nos dois modos, com a lista nova. |
Aceita também: React.HTMLAttributes<HTMLDivElement>.
Medidas
| Parte | Valor | Classe |
|---|---|---|
| Botão (título de uma linha) | 96px de altura em 1280px (medido) | p-5 md:p-6 |
| Seta | círculo de 32px | size-8 |
Tokens de cor
Os tokens semânticos que o código da peça usa, com o valor de hoje em app/globals.css.
| Token | Valor | Usado como |
|---|---|---|
--ink-inverse | #FAFAFA | bg-ink-inverse/10bg-ink-inverse/20bg-ink-inverse/5border-ink-inverse/10text-ink-inverse |
--border | #E5E5E5 | bg-borderborder-border |
--muted | #F4F6F9 | bg-mutedbg-muted/50 |
--card | #FFFFFF | bg-card |
--foreground | #1A1A1A | text-ink |
--ink-inverse-muted | #C7C7C7 | text-ink-inverse-muted |
--muted-foreground | #595959 | text-muted-foreground |
--ring | var(--footer-red)), decisão da revisão 2. Se outra peça precisar
* dele como superfície, o nome vira de papel (ex.: --surface-brand-strong). */
--footer-red: #7D1B1B | ring-ring |
--ring-inverse | #FAFAFA | ring-ring-inverse |
--secondary | #0B8434 | text-secondary |
Teclado
| Tecla | Faz |
|---|---|
| Tab | Vai de botão em botão; com um item aberto, o painel dele entra entre o botão e o próximo — medido. |
| EnterEspaço | Abre ou fecha o item em foco — medido. |
| ↑↓ | Não andam entre os itens — medido. |
Leitor de tela
O que é anunciado, e por quê.
- O botão fica dentro do cabeçalho — o padrão de acordeão da WAI-ARIA —, com
aria-expandedearia-controlsapontando para o painel. - O painel aberto recebe foco, achado da passada com leitor de tela: sem isso, o conteúdo recém-aberto ficava para trás. É decisão local, e não do padrão ARIA: custa uma parada a mais por item aberto.
- No escuro, o subtítulo é
--ink-inverse-muted: o verde media 3,18:1 ali — achado pela ficha do tema escuro, na E65.
O que sustenta o estado
Esta peça usa divulgacao. O estado dela é o de quem já foi ouvido com leitor de tela nesses padrões — e não uma passada sobre esta peça em específico.
Falta ouvir: divulgacao — ver docs/a11y-screen-reader-checklist.md.
Status e recursos
| Tipo | Recurso | Status |
|---|---|---|
| Código | app/components/molecules/Accordion.tsx | Disponível |
| Ficha | Storybook (abre em outra aba) | Disponível |
| Teste de acessibilidade | axe em npm run test | No gate de todo commit |
| Leitor de tela | Passada da E33 | Falta ouvir |
| Design | Figma | Desativado até a Fase 2 |
Histórico
Os últimos commits que tocaram o arquivo, lidos do git no build.
0298af82026-09-30E147: fechamento da Entrega 11 — REPORT.md, verificação cross-agent e o gate estático estrito
O histórico completo está no GitHub (abre em outra aba).