accessibility
Boas práticas de tabindex
Assinala valores positivos de tabindex e abuso de tabindex="-1". Valores positivos quebram a ordem do DOM; só 0 e -1 pertencem a markup de produção.
O que esta verificação faz
Analisa todos os elementos com atributo tabindex e reporta dois problemas:
- Valores positivos (
tabindex="1","2", etc.) — sobrepõem-se à ordem natural do DOM e quase sempre causam bugs de foco. - Abuso de
tabindex="-1"em elementos que deviam ser alcançáveis por teclado — erro comum em botões e links personalizados.
A verificação também nota quando tabindex é aplicado a elementos que já são focáveis nativamente (<a href>, <button>, <input>), onde costuma ser redundante.
Porque é importante
Um tabindex positivo cria um universo paralelo de teclado. O navegador percorre primeiro todos os valores positivos — por ordem numérica — e depois cai na ordem do DOM para tudo o resto. Adiciona tabindex="1" a um elemento e acabaste de o promover à frente de cada link, botão e campo de formulário da página. Adiciona-o a dez elementos e renumeraste manualmente a ordem de tab, o que significa que cada edição futura (um novo item de menu, um campo de formulário trocado) quebra silenciosamente a sequência.
A WCAG 2.4.3 (Focus Order) exige que o foco se mova numa ordem que preserve o significado e a operabilidade. A ordem visual deve corresponder à ordem de tab. Tabindex positivo quase sempre viola isto — utilizadores de teclado e de leitor de ecrã aterram em sítios surpreendentes, perdem contexto e abandonam o fluxo.
O tabindex="-1" tem o problema oposto quando mal usado: remove o elemento da sequência de tab por completo. Aplica-o a um botão e os utilizadores de teclado não conseguem chegar lá.
Como corrigir
Regra prática: em código de aplicação normal, os únicos valores aceitáveis são tabindex="0" e tabindex="-1", e só precisas deles em elementos não nativos.
Quando tabindex="0" está certo
Um widget interativo personalizado que o navegador não foca por defeito:
<!-- Tab personalizado num tablist -->
<div role="tab" tabindex="0" aria-selected="true">Visão geral</div>
<!-- Card que abre um modal ao clicar -->
<div role="button" tabindex="0" onclick="openModal()">
Ver detalhes
</div>
Prefere um <button> real sempre que possas — ganhas foco, ativação por Enter/Espaço e semântica para leitor de ecrã de graça. Usa tabindex="0" apenas quando o elemento nativo não serve.
Quando tabindex="-1" está certo
Um alvo de foco programático — um elemento que vais focar via JavaScript mas que não deve aparecer na sequência de tab:
<!-- Resumo de erros, focado ao submeter o formulário -->
<div id="errors" tabindex="-1" role="alert">
3 campos precisam de atenção
</div>
<!-- Container de modal, focado ao abrir -->
<div id="modal" tabindex="-1" role="dialog" aria-modal="true">
...
</div>
<!-- Alvo de skip-link (conteúdo principal) -->
<main id="main" tabindex="-1">...</main>
Depois em JS:
document.getElementById("errors").focus();
O -1 faz com que .focus() funcione sem inserir o elemento na ordem de Tab.
Quando tabindex positivo está certo
Quase nunca. O único caso defensável é integração com um formulário legacy onde não podes reorganizar o DOM e a ordem visual exige re-sequenciamento — e mesmo aí, corrigir o DOM é a resposta certa.
Lint na CI
Apanha isto antes da revisão:
bun add -D eslint-plugin-jsx-a11y
// eslint.config.js
import jsxA11y from "eslint-plugin-jsx-a11y";
export default [{
plugins: { "jsx-a11y": jsxA11y },
rules: {
"jsx-a11y/tabindex-no-positive": "error",
"jsx-a11y/no-noninteractive-tabindex": "warn",
},
}];
Para repositórios de HTML / templates vanilla, o axe-core apanha o mesmo problema na auditoria — vê falhas de acessibilidade no Lighthouse.
Corrigir uma página que já tem tabindex positivo
- Remove todos os atributos
tabindex="1+". - Percorre a página com o teclado. Anota saltos surpreendentes.
- Reorganiza o DOM para que a sequência natural corresponda à visual. Usa CSS (
order, posicionamento em grid) apenas para reposicionamento visual que não afete a ordem de leitura. - Volta a correr a auditoria.
Perguntas frequentes
Posso usar tabindex="0" para tornar um <div> clicável?
Podes, mas adicionar role="button" e handlers de Enter/Espaço é metade do trabalho — um <button> real é um elemento com tudo ligado. O padrão tabindex="0" + role="button" está reservado para casos em que precisas de um elemento não-botão (ex. um treeitem, um tab) que a spec não inclui como focável nativamente.
tabindex="-1" esconde o elemento dos leitores de ecrã?
Não. O tabindex="-1" apenas remove o elemento da navegação sequencial por teclado. Os leitores de ecrã ainda lá chegam pela sua própria navegação (cabeçalhos, landmarks, cursor virtual). Para esconder de tecnologia de apoio, usa aria-hidden="true" — mas nunca num elemento interativo.
E o tabindex rotativo dentro de um menu ou grid?
Esse é o padrão certo para widgets compostos (menus, tablists, grids). Exatamente um item tem tabindex="0", os restantes têm tabindex="-1", e as teclas de seta movem o foco trocando os valores. A verificação contempla isto: um único 0 mais muitos -1 dentro do mesmo role="menu" / role="tablist" / role="grid" não é assinalado.
Fontes
Última atualização 2026-05-11