Acessibilidade
O que todo componente do Camaleo garante em todos os estilos, como isso é testado e o contraste medido das cores de cada estilo.
// você vai aprender
- O que você ganha de todo componente sem trabalho extra
- Como a biblioteca é testada nos 13 estilos, em claro e escuro, com cinco cores de marca difíceis
- O contraste medido das cores de texto e de controle de cada estilo
- Onde a aparência autêntica e a acessibilidade discordam, e qual vence
A meta é WCAG 2.2 AA em todos os estilos, em claro e escuro, com qualquer cor de marca. Um estilo pode mudar forma, profundidade, tipografia e movimento; não pode tirar um nome, uma tecla ou um anel de foco.
O que todo componente garante
- Teclado. Cada componente segue um padrão do WAI-ARIA Authoring Practices e herda o modelo de teclado da primitiva por baixo (Base UI, ou React Aria nas datas). As setas andam dentro de um widget, o Tab passa de um widget a outro, o Esc fecha popups e devolve o foco ao gatilho.
- Foco. O foco é desenhado com
outline, nunca só combox-shadow, então sobrevive às cores forçadas. A cor dele,--cml-focus-ring, é testada com 3:1 ou mais contra a superfície em todos os estilos. - Nomes. Botões e regiões embutidos têm nome (fechar, limpar, mostrar senha, página 3…), que você traduz com o
MessagesProvider. Controles só com ícone que você monta ainda precisam dearia-label. - Alvos. Checkboxes, rádios, links da trilha e outros controles pequenos mantêm uma área de clique de pelo menos 24 × 24 px, mesmo nos estilos densos.
- Movimento reduzido. Reduz, não remove: com
prefers-reduced-motion, distâncias e escalas vão a zero, fades curtos ficam, painéis abrem de uma vez, o brilho do skeleton para e uma barra de progresso indeterminada pulsa no lugar. - Cores forçadas. Os componentes reservam uma borda transparente que aparece no alto contraste do Windows, e os estados selecionados passam para cores do sistema (
Highlight,CanvasText). - Transparência reduzida.
glassecupertinotrocam as superfícies translúcidas por opacas comprefers-reduced-transparency: reducee em navegadores sembackdrop-filter. - Direita para a esquerda. Os componentes invertem com o
DirectionProvider; veja Idiomas e RTL.
Como é testado
Os testes ficam em packages/react/test e rodam num navegador de verdade (Vitest em modo navegador com Playwright).
- A matriz (
matrix.test.tsx) renderiza uma vitrine com todos os componentes em 13 estilos × claro e escuro × 5 cores de marca difíceis (#2563eb,#facc15,#22d3ee,#ff7f50e#808080): 130 escopos. Em cada um, roda o axe com as regras A/AA da WCAG 2.0, 2.1 e 2.2, confere que o anel de foco e a borda dos controles chegam a 3:1, que os tons de perigo, sucesso, aviso e informação chegam a 4,5:1, e depois abre 17 popups (diálogo, menu, select, seletor de data, toast…), um por vez, para conferir que cada um mantém o estilo pelo portal e também passa no axe. São 2.340 execuções. - Texto sobre imagem. O axe não mede texto sobre gradiente e vidro e marca o caso como "incompleto". A matriz mede esses nós em pixels reais (
pixel-contrast.ts): duas capturas, com e sem o texto, e o pior pixel das letras contra o fundo logo atrás. - Teclado (
keyboard.test.tsx): 30 testes que usam os componentes com teclas de verdade, do Espaço num checkbox ao PageUp num calendário. - As docs. Cada página deste site, com os exemplos ao vivo, passa pelo axe em 4 estilos, claro e escuro, em larguras de desktop, tablet e celular, nos dois idiomas.
Relatório de contraste
A tabela abaixo é medida, não digitada: o apps/docs/scripts/contrast-report.mjs carrega o tokens.css e o CSS de cada estilo num navegador, resolve os tokens com cada uma das cinco cores de marca da matriz e guarda a menor razão das cinco. Os números são arredondados para baixo, então 4,499 aparece como 4,49.
O que ele mede, sempre contra a cor da página do estilo, --cml-surface, salvo indicação:
- Texto, 4,5:1 ou mais:
--cml-fg(texto corrido),--cml-fg-secondary,--cml-fg-muted,--cml-accent-text(texto e links na cor da marca),--cml-accent-fgsobre--cml-accent(texto num botão sólido da marca) e os tons--cml-danger,--cml-successe--cml-warning. - Não texto, 3:1 ou mais:
--cml-border-control(a borda de campos e opções) e--cml-focus-ring.
Ele não mede texto sobre superfícies elevadas, estados de hover nem painéis de vidro sobre o fundo; a matriz cobre esses casos nos componentes renderizados.
| Estilo | Modo | fg | fg-secondary | fg-muted | accent-text | danger | success | warning |
|---|---|---|---|---|---|---|---|---|
| minimal | claro | 19,89 | 7,72 | 4,83 | 6,93 | 6,57 | 5,01 | 6,85 |
| escuro | 19,06 | 13,46 | 7,76 | 9,94 | 7,14 | 11,41 | 12,99 | |
| neumorphism | claro | 10,04 | 5,15 | 5,15 | 5,75 | 6,25 | 5,79 | 4,91 |
| escuro | 11,20 | 7,67 | 6,12 | 6,90 | 7,27 | 8,98 | 8,27 | |
| retro-pixel | claro | 17,09 | 8,36 | 8,36 | 6,63 | 6,47 | 6,22 | 6,10 |
| escuro | 16,16 | 10,34 | 10,34 | 9,20 | 5,92 | 9,59 | 12,71 | |
| editorial | claro | 16,31 | 8,10 | 6,41 | 6,21 | 6,76 | 5,72 | 5,66 |
| escuro | 14,81 | 10,20 | 7,99 | 9,18 | 7,57 | 9,21 | 9,34 | |
| neubrutalism | claro | 17,35 | 10,72 | 6,58 | 6,37 | 6,04 | 4,61 | 6,29 |
| escuro | 16,20 | 11,63 | 7,41 | 9,28 | 6,67 | 10,66 | 12,13 | |
| retro | claro | 11,54 | 8,74 | 5,35 | 5,39 | 5,28 | 4,52 | 4,70 |
| escuro | 9,85 | 8,19 | 6,32 | 5,51 | 5,44 | 6,51 | 7,70 | |
| glass | claro | 15,78 | 9,15 | 7,65 | 6,13 | 6,61 | 6,30 | 6,05 |
| escuro | 17,42 | 12,85 | 8,62 | 9,53 | 8,55 | 10,95 | 12,46 | |
| swiss | claro | 21,00 | 15,13 | 6,68 | 6,93 | 6,57 | 5,01 | 6,85 |
| escuro | 21,00 | 15,31 | 8,32 | 10,49 | 7,53 | 12,05 | 13,71 | |
| progressive | claro | 15,62 | 8,46 | 5,20 | 6,19 | 5,87 | 5,50 | 6,11 |
| escuro | 15,66 | 11,11 | 6,80 | 8,66 | 6,22 | 9,95 | 11,32 | |
| clay | claro | 12,94 | 8,80 | 6,16 | 6,49 | 6,15 | 6,67 | 6,41 |
| escuro | 15,16 | 11,46 | 7,11 | 8,57 | 6,88 | 10,89 | 11,51 | |
| material | claro | 16,14 | 8,82 | 6,06 | 6,59 | 6,12 | 6,19 | 6,12 |
| escuro | 14,45 | 10,86 | 8,08 | 9,29 | 10,90 | 10,63 | 10,86 | |
| cupertino | claro | 18,81 | 9,80 | 5,36 | 6,21 | 4,82 | 5,37 | 6,14 |
| escuro | 21,00 | 13,80 | 9,49 | 10,49 | 8,61 | 10,38 | 14,87 | |
| y2k | claro | 14,65 | 9,05 | 5,95 | 5,63 | 5,34 | 5,79 | 5,56 |
| escuro | 16,71 | 11,53 | 8,15 | 9,33 | 6,70 | 10,72 | 12,20 |
| Estilo | Modo | accent-fgsobre accent | border-control | focus-ring |
|---|---|---|---|---|
| minimal | claro | 4,85 | 3,24 | 6,93 |
| escuro | 7,45 | 4,11 | 9,94 | |
| neumorphism | claro | 4,85 | 3,11 | 5,75 |
| escuro | 7,45 | 3,71 | 6,90 | |
| retro-pixel | claro | 4,85 | 17,09 | 6,63 |
| escuro | 7,45 | 16,16 | 9,20 | |
| editorial | claro | 16,31 | 16,31 | 6,21 |
| escuro | 14,81 | 14,81 | 9,18 | |
| neubrutalism | claro | 5,35 | 17,35 | 6,37 |
| escuro | 5,35 | 16,20 | 9,28 | |
| retro | claro | 9,80 | 10,88 | 5,39 |
| escuro | 4,85 | 4,21 | 5,51 | |
| glass | claro | 4,85 | 4,20 | 6,13 |
| escuro | 7,45 | 5,88 | 9,53 | |
| swiss | claro | 4,85 | 21,00 | 21,00 |
| escuro | 7,45 | 21,00 | 21,00 | |
| progressive | claro | 4,85 | 3,34 | 6,19 |
| escuro | 5,35 | 4,63 | 8,66 | |
| clay | claro | 4,85 | 3,66 | 6,49 |
| escuro | 7,45 | 4,78 | 8,57 | |
| material | claro | 4,85 | 4,25 | 6,59 |
| escuro | 10,49 | 5,83 | 9,29 | |
| cupertino | claro | 4,85 | 3,72 | 4,35 |
| escuro | 4,85 | 6,44 | 5,35 | |
| y2k | claro | 4,85 | 3,58 | 5,63 |
| escuro | 5,35 | 5,02 | 9,33 |
Todos os pares atingem a meta em todos os estilos e modos. Rode node scripts/contrast-report.mjs em apps/docs depois de mudar um estilo para atualizar os números.
Autêntico ou acessível
Alguns estilos falham no contraste por natureza: o neumorphism não tem bordas, o vidro põe texto sobre o que estiver atrás, um sistema operacional clássico desenha um retângulo de foco pontilhado de 1 px. O projeto decidiu (decisão 9 do plano) que todo estilo é acessível por padrão, e que uma variante autêntica seria opcional e marcada como "não garante AA". Nenhuma variante autêntica existe ainda.
| Estilo | A aparência autêntica | O que o Camaleo faz por padrão |
|---|---|---|
neumorphism | Controles moldados só por luz e sombra, sem bordas | Borda de 1 px com 3:1 ou mais em campos e opções; os botões têm o nome no texto |
glass | Painéis translúcidos sobre qualquer fundo | Manchas do fundo claras (no escuro, escuras) o bastante para o texto manter 4,8:1 no pico; opaco com transparência reduzida |
clay | Peças pastel macias, sem contorno | Campos e opções mantêm uma borda de 3:1 ou mais |
cupertino | Cromo fosco em menus e popups | Vidro escuro a 94% de opacidade para o texto manter 4,5:1; alternativa opaca |
y2k | Botões "jelly" brilhantes com texto sobre o brilho | O texto fica só sobre o destaque liso; o brilho fica acima e abaixo dele |
retro | Retângulo de foco pontilhado | Mantido dentro de botões, abas e toggles (na cor do rótulo); os campos ganham um anel sólido de 2 px fora do bisel |
Um token ou regra que você muda pode quebrar o que os testes garantem. Depois de qualquer mudança de cor, rode a matriz de novo (pnpm --filter @camaleo/react test) ou pelo menos este relatório.
Antes de publicar
- Botões só com ícone que você acrescenta têm
aria-label - Cores próprias vêm de tokens, então mantêm os pares testados
- Animações que você acrescenta respeitam
prefers-reduced-motion - Popups novos mantêm o estilo (use
PortalScopenos seus portais) - O relatório de contraste e a matriz continuam passando depois de mudar um estilo