Por Que Acessibilidade Importa
Segundo a OMS, mais de 1 bilhão de pessoas no mundo vivem com alguma deficiência. No Brasil, são 18 milhões com deficiência visual. Acessibilidade não é opcional; é responsabilidade.
Mas além da ética, há benefícios práticos: sites acessíveis têm melhor SEO (semântica HTML), melhor usabilidade em dispositivos móveis, e são mais fáceis de manter.
1. Semântica HTML: A Base de Tudo
O erro mais comum é usar <div> para tudo. HTML semântico comunica significado para leitores de tela e crawlers:
<!-- ❌ Não faça isso -->
<div class="header">
<div class="nav">
<div class="nav-item" onclick="navigate()">Home</div>
</div>
</div>
<!-- ✅ Use a semântica correta -->
<header>
<nav>
<ul>
<li><a href="/">Home</a></li>
</ul>
</nav>
</header>
2. ARIA: Quando HTML Não É Suficiente
Componentes customizados como modais, tooltips e accordions precisam de atributos ARIA para comunicar seu estado:
// Modal acessível
function Modal({ isOpen, onClose, title, children }) {
return (
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
aria-describedby="modal-desc"
>
<h2 id="modal-title">{title}</h2>
<div id="modal-desc">{children}</div>
<button
onClick={onClose}
aria-label="Fechar modal"
>
<X aria-hidden="true" />
</button>
</div>
);
}
3. Gerenciamento de Foco
Quando um modal abre, o foco deve ir para dentro do modal. Quando fecha, deve voltar ao elemento que o abriu:
import { useEffect, useRef } from "react";
function AccessibleModal({ isOpen, onClose, triggerRef, children }) {
const modalRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (isOpen) {
// Move o foco para o modal
modalRef.current?.focus();
} else {
// Retorna o foco ao botão que abriu
triggerRef.current?.focus();
}
}, [isOpen]);
// Trap focus dentro do modal
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape") onClose();
// implementar Tab trap...
};
}
4. Radix UI: Acessibilidade Por Padrão
O Radix UI implementa todos esses padrões automaticamente. É por isso que uso ele em todos os meus projetos:
import * as Dialog from "@radix-ui/react-dialog";
// Todos os comportamentos de acessibilidade já estão implementados:
// - Foco automático ao abrir
// - Foco trap dentro do modal
// - Fechamento com Escape
// - aria-modal, role="dialog" aplicados automaticamente
function MyModal() {
return (
<Dialog.Root>
<Dialog.Trigger asChild>
<Button>Abrir Modal</Button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay />
<Dialog.Content>
<Dialog.Title>Meu Modal</Dialog.Title>
<Dialog.Description>Descrição acessível</Dialog.Description>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}
5. Cores e Contraste
WCAG AA exige contraste mínimo de 4,5:1 para texto normal e 3:1 para texto grande. Ferramentas que uso:
- WebAIM Contrast Checker
- Extensão "axe DevTools" no Chrome
- Audit do Lighthouse ("Accessibility")
Testando com Leitores de Tela
Teste seu site com:
- NVDA (Windows, gratuito) + Chrome
- VoiceOver (macOS/iOS, nativo) + Safari
- TalkBack (Android, nativo) + Chrome
Navegue pelo site usando apenas o teclado. Se você não conseguir completar uma tarefa só com Tab + Enter + Setas, seu usuário de teclado também não conseguirá.
Gostou do conteúdo?
Se você precisar de ajuda aplicando essas técnicas no seu projeto, estou disponível para consultoria.

Autor
Paulo Reducino
Desenvolvedor Frontend com 5+ anos de experiência em React, Next.js e TypeScript. Especialista em performance, SEO e acessibilidade. Atualmente na Vizuh (Londres, UK).
