Fundamentos
Acessibilidade
WCAG AA por padrão. Cada componente do sistema implementa um padrão ARIA documentado — não é decoração, é contrato.
Princípios
Quatro pilares
WCAG 2.2 organiza acessibilidade em perceptível, operável, compreensível e robusto. Sinapse persegue nível AA como mínimo.
Perceptível
Alternativa textual em ícones, contraste mínimo 4.5:1 em texto, focus visível, conteúdo não depende só de cor.
Operável
Tudo via teclado. Tab/Shift+Tab cobre rota completa. Sem trap de foco em overlays. Atalhos previsíveis.
Compreensível
Linguagem clara, ordem lógica, erros explicados, label associado a input via for/id ou aria-labelledby.
Robusto
HTML válido, ARIA semântico (button vs role=button), parsing confiável por leitores de tela.
Padrões ARIA
Implementados no sistema
Cada componente segue um padrão da APG (ARIA Authoring Practices Guide). Nada é improviso.
| Componente | Padrão APG | Comportamento garantido |
|---|---|---|
| Select | combobox 1.2 | Listbox custom, navegação ↑↓ Home/End, Enter seleciona, Esc fecha, type-ahead. |
| Modal | dialog (modal) | Trap de foco, Esc fecha, retorna foco ao trigger, aria-labelledby pro título. |
| Tabs | tablist + tabpanel | ↔ entre tabs, Home/End extremos, aria-selected, painel via aria-labelledby. |
| Side Panel | tablist (interno) | Drawer com tabs internas. Esc fecha, retorna foco ao trigger. |
| Tooltip | tooltip + aria-describedby | Hover e focus revelam, conteúdo associado ao trigger via describedby. |
| Toast | live region (status) | role=status anuncia transient sem interromper. role=alert para erros críticos. |
| Callout | role=status | Anúncio polite. Não rouba foco. Mudança de status comunicada. |
| Breadcrumb | nav + ol + aria-current | Landmark navegacional, item atual marcado, separadores aria-hidden. |
| Pagination | nav + button labels | aria-label descritivo em cada botão (Página 3, Próxima página). |
| Field | label + aria-describedby | Label associado via for/id, support text e error via describedby, aria-invalid em erro. |
Foco
Visível, sempre
Foco invisível é foco quebrado. Sinapse usa focus-visible para distinguir teclado de mouse — nada some.
Padrão de focus ring
focus-visible:outline-none
focus-visible:ring-2
focus-visible:ring-primary
focus-visible:ring-offset-2 Por que focus-visible
:focus mostra ring após click do mouse — ruidoso. :focus-visible só mostra quando navegação por teclado, mantendo legibilidade visual sem sacrificar a11y.
Contraste
WCAG mínimos
| Elemento | AA | AAA | Token Sinapse |
|---|---|---|---|
| Texto normal (<18px) | 4.5:1 | 7:1 | text-foreground · text-muted-foreground |
| Texto grande (≥18px ou 14px bold) | 3:1 | 4.5:1 | text-muted-foreground (mínimo permitido) |
| UI (border, ícone) | 3:1 | — | border-border · icon-default |
| Focus ring | 3:1 vs adjacente | — | ring-primary |
Teclado
Atalhos universais
Próximo elemento focável
Anterior
Ativar (button, link, listbox option)
Toggle (checkbox, switch) ou ativar (button)
Fechar overlay (modal, panel, tooltip, dropdown)
Navegação dentro de listbox, radio group
Navegação entre tabs
Primeiro / último em coleções
Anti-patterns
O que nunca fazer
<div onclick="..."> Div não recebe foco e não responde a teclado.
→ <button> ou role=button + tabindex=0 + onkeydown placeholder como label Some ao digitar, baixo contraste, leitor de tela inconsistente.
→ <label> visível associado via for/id outline: none Remove foco visível — quebra navegação por teclado.
→ focus-visible:ring-2 ring-primary aria-label em texto visível Sobrescreve o texto pra leitores de tela. Confunde.
→ Deixar texto natural ser o accessible name cor como único sinal Daltonismo, modo monocromático, baixa visão.
→ Cor + ícone + texto (ex: ✓ Sucesso, ⚠ Aviso) Ferramentas
Validação contínua
axe DevTools
Auditoria automatizada no browser. Detecta ~57% das issues.
Lighthouse
Score de acessibilidade integrado ao Chrome DevTools.
NVDA / VoiceOver
Teste real com leitor de tela. Insubstituível.
Teclado apenas
Desconectar mouse e completar tarefa. Detecta trap, ordem ilógica.