Um gate de acessibilidade de dois segundos que roda a cada push
Eu já tinha um harness do vitest que renderiza meus componentes Svelte e faz mock das chamadas pro Rust. Adicionar checagens de acessibilidade acabou saindo quase de graça: apontar o axe-core para cada tela renderizada e afirmar que ele não encontra nada. Agora todo push roda o axe sobre o wizard de onboarding, o dashboard tanto vazio quanto cheio, e os dois diálogos, e falha o build se qualquer um deles ganhar um label faltando ou um atributo ARIA quebrado. Leva uns dois segundos.
Restringi de propósito às regras WCAG A e AA de verdade. O axe vem com uma pilha de regras de “boa prática” em cima do padrão, e algumas delas, tipo “todo pedaço de conteúdo precisa estar dentro de uma landmark region,” disparam o tempo todo quando você renderiza um único diálogo isolado em vez de uma página inteira. Isso é ruído, não um problema real, então configurei o axe pra rodar só as regras marcadas como WCAG. O gate sinaliza o que está de fato errado e fica quieto sobre o que só está errado fora de contexto.
Aqui vai a parte em que quero ser honesto, porque é fácil vender isso demais. Os testes rodam em jsdom, que constrói um DOM mas nunca faz layout nem pinta nada. Isso significa que a primeira checagem de acessibilidade em que todo mundo pensa, contraste de cor, não roda aqui. O axe tenta, falha em conseguir um canvas pra medir pixels, e silenciosamente reporta o resultado como “incompleto” em vez de passou ou falhou. Então uma regressão de contraste passa reto por esse gate. Mesma história pros estilos de focus-visible. O que eu de fato tenho é um gate estrutural: nomes, roles, labels, headings, associações de formulário. Valioso, e a maior parte do que regride no dia a dia, mas não o quadro completo.
Em vez de fingir o contrário, escrevi a lacuna no comentário do código e registrei a outra metade contra minha issue de dark mode. Contraste só começa de fato a quebrar quando você começa a trocar cores de tema, e é exatamente isso que dark mode é. Quando eu construir isso, vou adicionar uma passada real do axe em navegador que checa contraste nos dois temas. Ferramenta certa, momento certo.
Duas pequenas armadilhas no caminho. Os tipos TypeScript da biblioteca de matchers foram escritos pra uma versão mais antiga do vitest e simplesmente não se conectavam à atual, então meu type-checker insistia que a asserção não existia mesmo ela rodando de boa. Uma declaração de tipo de quatro linhas, minha mesmo, resolveu. E antes de confiar num resultado verde, alimentei o axe com um trecho deliberadamente quebrado, uma imagem sem alt text e um botão vazio, pra confirmar que ele de fato reporta violações. Um teste de acessibilidade que passa mas não consegue falhar é pior que nenhum teste, porque te diz que você está coberto quando não está.
O desfecho bom: minhas telas já existentes voltaram limpas, então não havia nada pra corrigir, só um gate pra manter assim.
Leitura relacionada
Dark mode, e o teste de navegador que reagiu
light-dark() deixou o CSS fácil. Três falhas de tooling de teste deixaram o gate de contraste difícil. Depois, screenshots reais provaram que o gate verde não contava a história toda.
A pasta que ficou parada
Uma revisão de código encontrou um invariante que a feature de adotar/importar do Greenhouse quebrava silenciosamente, e a correção que tornou tudo chato de novo.
O diálogo que se fechava sozinho antes de eu conseguir usá-lo
Um callback renomeado para algo genérico manteve seu significado específico de wizard, e o mesmo sinal queria dizer duas coisas diferentes para dois ouvintes diferentes.
Você também pode achar útil
Proton Drive
Armazenamento em nuvem criptografado, da equipe por trás do Proton Mail.
Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).
Saiba maisProton Mail
E-mail criptografado de ponta a ponta, com arquitetura de acesso zero.
Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).
Saiba maiseSIM Airalo
eSIM de dados local para viagens - sem necessidade de trocar um SIM físico.
Este é meu link de indicação da Airalo. Você recebe um desconto no seu primeiro eSIM e eu ganho crédito da Airalo para o meu.
Saiba mais