icardb
Acessibilidade web (a11y) na prática: WCAG, ARIA e testes que funcionam
Voltar para artigosPROGRAMAÇÃO

Acessibilidade web (a11y) na prática: WCAG, ARIA e testes que funcionam

Por Lucas Mendes 7 min de leitura

Acessibilidade não é um extra para 'quando sobrar tempo': é requisito de qualidade de software — e, em muitos contextos, requisito legal. No Brasil, a Lei nº 13.146/2015 (Estatuto da Pessoa com Deficiência) e a Lei nº 14.133/2021 para compras públicas exigem acessibilidade em sites, e o modelo-ABNT NBR 17225:2025 passou a normatizar o tema alinhado ao WCAG. Este artigo é o essencial técnico que todo front-end precisa dominar.

WCAG em cinco minutos

As Web Content Accessibility Guidelines organizam-se em quatro princípios (POUR): Perceptível, Operável, Compreensível e Robusto. Os níveis de conformidade são A (mínimo), AA (alvo padrão de mercado e da maioria das legislações) e AAA (aspiracional). Na prática, seu objetivo é WCAG 2.2 nível AA.

Requisito (WCAG 2.2 AA)CritérioComo verificar
Contraste de texto normalMínimo 4,5:1DevTools → painel de contraste ou WebAIM Contrast Checker
Contraste de texto grande (18pt+)Mínimo 3:1Idem
Foco visívelIndicador de foco nunca removidoTab pela página inteira
Alvo de toqueMínimo 24×24 CSS px (2.2)Inspeção de tamanho de botões/links
Texto alternativoalt em toda imagem informativaRevisão + axe
Redimensionar textoLayout funciona com zoom de 200%Zoom do browser

HTML semântico: 80% do trabalho

A regra número um de a11y: use o elemento nativo para o propósito nativo. Um <button> já vem com foco, ativação por Enter e Espaço, e papel correto na árvore de acessibilidade. Um <div onclick> não vem com nada disso — e você terá que reimplementar cada comportamento manualmente (e provavelmente errar).

html
<!-- Ruim: inacessível em todos os sentidos -->
<div class="btn" onclick="salvar()">Salvar</div>

<!-- Bom: foco, teclado, papel e nome acessível de graça -->
<button type="button" onclick="salvar()">Salvar</button>

<!-- Estrutura que leitores de tela usam para navegar -->
<header>...</header>
<nav aria-label="Principal">...</nav>
<main>
  <h1>Título único da página</h1>
  <article>...</article>
</main>
<footer>...</footer>

Landmarks (header, nav, main, footer) permitem que usuários de leitor de tela pulem direto para a região desejada. Hierarquia de headings (h1 → h2 → h3, sem pular níveis) funciona como sumário navegável. Links devem fazer sentido fora de contexto: 'Leia a documentação de acessibilidade' em vez de 'clique aqui'.

ARIA: a primeira regra é não usar

A primeira regra do ARIA, da própria especificação: se existe elemento HTML nativo com a semântica desejada, use-o em vez de adicionar role/aria-*. ARIA não adiciona comportamento — apenas descreve. Um role='button' numa div não a torna clicável por teclado; você ainda precisará implementar keydown, foco e estados. Onde ARIA é legítimo e necessário:

  • aria-label / aria-labelledby: nome acessível para ícones-only, ex. <button aria-label='Fechar'>×</button>.
  • aria-live: anuncia mudanças dinâmicas (toasts, contadores) sem mover o foco.
  • aria-expanded / aria-controls: estados de acordeões e menus customizados.
  • aria-invalid + aria-describedby: vincula erro de formulário ao campo.
  • aria-hidden='true': esconde conteúdo puramente decorativo de leitores de tela.
html
<!-- Campo com erro anunciado corretamente -->
<label for="email">E-mail</label>
<input id="email" name="email" type="email"
       aria-invalid="true" aria-describedby="email-erro" />
<p id="email-erro" role="alert">Informe um e-mail válido.</p>

<!-- Notificação dinâmica anunciada sem roubar foco -->
<div aria-live="polite" id="toast"></div>

Navegação por teclado e gestão de foco

Todo elemento interativo precisa ser alcançável e operável por teclado. Dois padrões merecem atenção: modais (o foco deve entrar no modal ao abrir, ficar preso dentro dele — focus trap — e voltar ao elemento que o abriu ao fechar) e o link 'Pular para o conteúdo' como primeiro item focável da página.

css
/* NUNCA faça: outline: none; sem substituto */

/* Foque com estilo claro apenas para teclado */
:focus-visible {
  outline: 3px solid #2563eb;
  outline-offset: 2px;
}

/* Skip link: invisível até receber foco */
.skip-link {
  position: absolute;
  left: -9999px;
}
.skip-link:focus {
  position: static;
  left: auto;
}

Testes: automatizado cobre ~30%, o resto é manual

Ferramentas como axe detectam problemas objetivos (contraste, alt ausente, label faltando), mas não julgam qualidade do alt, ordem lógica de leitura ou usabilidade real com leitor de tela. O fluxo maduro combina as duas frentes:

ts
// Teste automatizado com axe-core + Playwright
import { test, expect } from "@playwright/test";
import AxeBuilder from "@axe-core/playwright";

test("página inicial sem violações críticas de a11y", async ({ page }) => {
  await page.goto("/");
  const resultado = await new AxeBuilder({ page })
    .withTags(["wcag2a", "wcag2aa"])
    .analyze();
  const criticas = resultado.violations.filter(
    (v) => v.impact === "critical" || v.impact === "serious"
  );
  expect(criticas).toEqual([]);
});
  • Automatizado: axe-core no CI, Lighthouse (score de acessibilidade), eslint-plugin-jsx-a11y no lint.
  • Manual: navegue a página inteira só com Tab/Shift+Tab/Enter/Espaço.
  • Leitor de tela: NVDA (Windows, gratuito) ou VoiceOver (macOS/iOS) — 15 minutos de uso revelam problemas que nenhum scanner vê.
  • Zoom: teste 200% e 400% de zoom e prefers-reduced-motion para animações.

Overlays de 'acessibilidade instantânea' (widgets que prometem conformidade com uma linha de JavaScript) não tornam um site conforme WCAG e são criticados por especialistas e por usuários com deficiência. Não há atalho que substitua markup correto.

Conclusão

Acessibilidade é, na maior parte, fazer o básico de HTML com seriedade: elementos nativos, landmarks, labels, contraste AA, foco visível e teste com teclado. ARIA entra pontualmente onde o HTML não alcança. Automatize o que é detectável em CI e reserve tempo para teste manual com leitor de tela — é a combinação que entrega um produto realmente utilizável por todas as pessoas.

Perguntas frequentes

+Qual nível de WCAG devo cumprir?

WCAG 2.2 nível AA é o alvo padrão de mercado e o referenciado pela maioria das legislações e editais, incluindo o modelo brasileiro NBR 17225. AAA é aspiracional e raramente exigido por inteiro.

+Imagens decorativas precisam de alt?

Sim, mas vazio: alt="". Isso instrui o leitor de tela a ignorá-la. Omitir o atributo alt faz alguns leitores anunciarem o nome do arquivo — pior que a ausência.

+Teste automatizado garante conformidade?

Não. Estudos do próprio projeto axe indicam que ferramentas automáticas detectam algo entre 30% e 40% dos problemas. Elas são excelentes para regressão em CI, mas avaliação manual com teclado e leitor de tela continua indispensável.

+Divs com role='button' são aceitáveis?

Funcionam se você reimplementar todo o comportamento nativo (tabindex, keydown de Enter e Espaço, estados aria-pressed/disabled), mas quase sempre é menos código — e menos bugs — usar <button> diretamente.

Fontes consultadas

Revisão editorial: publicado em . Última revisão em . Conteúdo educativo, sem patrocínio das ferramentas citadas.

Crédito da imagem: Foto: Lucas Mendes / Gerado por IA (Licença Editorial)

Leia também