17 de Janeiro de 2025
Componentes React acessíveis
Como construir componentes interativos em React preservando semântica, foco e comportamento nativo.
Andrews Ribeiro
Founder & Engineer
4 min Intermediario Frontend
O problema
Tem um erro muito comum em frontend moderno:
o time cria um componente React bonito, flexível e cheio de props, mas destrói a base da interação no processo.
Aí aparecem coisas como:
divfingindo ser botãospanclicável comonClicktabIndex={0}usado para compensar estrutura ruimrole="button"tentando salvar o que já nasceu torto
Na tela isso até pode parecer aceitável.
Mas o componente deixa de herdar comportamento que o navegador já resolvia bem.
E o pior: quando esse erro entra em design system ou biblioteca interna, ele se espalha pelo produto inteiro.
Modelo mental
Pensa assim:
componente acessível não é uma casca visual com atributos extras. É uma abstração que preserva a intenção e o comportamento do elemento certo.
Se a abstração obriga você a reconstruir com JavaScript o que o HTML nativo já faria de graça, tem uma boa chance de ela estar piorando a base.
Em React, isso importa ainda mais porque componente vira API de time.
Então a pergunta não é só:
- “esse componente funciona aqui?”
A pergunta melhor é:
- “esse componente continua seguro quando outras pessoas começarem a usá-lo em contextos diferentes?”
Quebrando o problema
Comece pela intenção, não pela aparência
Antes de estilizar, animação ou prop API, vale responder:
- isso executa uma ação na mesma tela?
- isso navega para outro lugar?
- isso abre ou fecha algum contexto?
Se executa ação, o ponto de partida costuma ser <button>.
Se navega, costuma ser <a>.
Quando você começa pela aparência e não pela intenção, a semântica vira efeito colateral.
Elemento nativo compra muito comportamento de uma vez
Um botão nativo já traz:
- foco
- ativação por teclado
- papel semântico claro
- integração melhor com tecnologias assistivas
Quando você troca isso por div, precisa reconstruir quase tudo. E quase sempre reconstrói pela metade.
API flexível demais costuma esconder custo
Esse é um ponto importante em React.
Tem componente que aceita:
asChildas- qualquer tag
- qualquer prop
Isso pode ser útil.
Mas também pode facilitar o uso errado sem ninguém perceber cedo.
Se o componente permite virar qualquer coisa sem proteger comportamento essencial, ele parece poderoso e ao mesmo tempo fica perigoso.
aria-* não corrige fundação ruim
ARIA é útil quando você precisa descrever algo que o HTML sozinho não expressa bem.
Mas ARIA não transforma uma base ruim em base boa.
Se um botão virou div, o primeiro problema não é falta de aria-label.
O primeiro problema é que você abandonou o elemento certo.
Componentes de estado exigem clareza extra
Quando o componente muda de estado, o usuário precisa perceber isso.
Exemplos comuns:
- botão toggle
- acordeão
- menu expansível
- modal trigger
Nesses casos, não basta clicar e torcer para o visual explicar tudo.
Você precisa pensar em:
- estado exposto de forma coerente
- foco previsível
- nome acessível claro
- relação entre gatilho e conteúdo
Exemplo simples
Imagina um componente IconButton para abrir um modal de busca.
Versão ruim:
function IconButton({ onClick, children }) {
return <div onClick={onClick}>{children}</div>;
}
Ele parece flexível.
Mas já começou devendo:
- não é botão nativo
- não herda comportamento de teclado corretamente
- não comunica papel com clareza
- vai exigir remendo sempre que o time quiser usá-lo direito
Versão melhor:
function IconButton({ onClick, label, children }) {
return (
<button type="button" onClick={onClick} aria-label={label}>
{children}
</button>
);
}
Agora a abstração preserva a base certa.
Depois disso você pode discutir estilo, variante, tamanho, loading, disabled e o resto da API. Mas a fundação já não está errada.
Erros comuns
- Criar componente base usando
divpor conveniência. - Achar que
role="button"resolve tudo. - Testar abstração só no clique e ignorar teclado.
- Expor API tão genérica que qualquer uso errado fica fácil.
- Usar
aria-*cedo demais em vez de corrigir semântica e comportamento. - Pensar em acessibilidade como adaptação final, não como requisito de modelagem.
Como um senior pensa
Quem tem mais experiência costuma desconfiar da própria abstração.
Principalmente em componente compartilhado.
A pergunta deixa de ser:
- “deu para encapsular?”
e vira:
- “essa encapsulação preserva o contrato nativo ou está escondendo uma dívida que o time vai pagar depois?”
Senioridade aqui aparece em duas coisas:
- escolher o elemento base com mais disciplina
- limitar a API para tornar o uso correto mais fácil que o uso errado
Isso é importante porque componente reutilizável multiplica qualidade quando nasce bem e multiplica problema quando nasce torto.
O que o entrevistador quer ver
Quando esse tema aparece em entrevista, o avaliador normalmente quer perceber se você:
- entende que abstração de UI não pode apagar semântica
- escolhe elemento pelo papel funcional
- pensa em foco e teclado como parte do contrato do componente
- sabe diferenciar uso legítimo de ARIA e remendo tardio
Uma resposta forte costuma soar assim:
Em componente React eu tento preservar o máximo possível do comportamento nativo. Se a abstração me faz reconstruir tecla, foco e papel semântico manualmente, eu já considero que talvez comecei do elemento errado. A API precisa ajudar o time a usar o componente certo do jeito certo.
Componente acessível não é o que recebe remendo no fim. É o que já nasce difícil de usar errado pelo time.
Se a abstração quebra o comportamento nativo, ela não está simplificando. Está terceirizando problema.
Resumo rápido
O que vale manter na cabeça
- Componente acessível começa no elemento certo, não em uma pilha de remendos com `role`, `tabIndex` e `aria-*`.
- Abstração boa preserva comportamento nativo em vez de reconstruí-lo mal com JavaScript.
- API de componente também é responsabilidade de acessibilidade, porque um erro ali escala para o time inteiro.
- Em entrevista, resposta forte mostra critério de modelagem de componente, não só conhecimento de atributo.
Checklist de pratica
Use isto ao responder
- Consigo explicar quando meu componente deveria renderizar `<button>`, `<a>` ou outro elemento nativo?
- Sei identificar quando uma abstração React está apagando comportamento nativo importante?
- Consigo revisar uma API de componente pensando em teclado, foco e estado anunciável?
- Sei dizer quando `aria-*` descreve algo real e quando está tentando esconder uma base errada?
Você concluiu este artigo
Próximo passo
Semântica e estrutura Próximo passo →Compartilhar esta página
Copie o link manualmente no campo abaixo.