Percurso da tecla Tab

Pressione Tab numa página web e o foco salta para o próximo link, botão ou campo. Esta verificação escreve o percurso inteiro para qualquer endereço e aponta os controles que ficaram de fora.

Uma passada pela página e por no máximo cinco das folhas de estilo dela. Nada é guardado.

Ou experimente mozilla.org, wikipedia.org

O foco é o cursor do teclado

Um mouse pode apontar para qualquer lugar. Um teclado só pode estar “em” um elemento por vez, e essa posição se chama foco. Tab o move para a frente, Shift+Tab para trás, Enter segue um link, Espaço pressiona um botão. Pessoas com tremores ou movimentos limitados das mãos navegam assim. Também navegam assim os usuários de leitores de tela, quem usa acionadores ou controle por voz, e qualquer pessoa que preenche formulários rápido.

O navegador deduz o que pode receber foco só pelo HTML. Links com href, botões, campos de formulário, <summary>, players de mídia com controles e frames estão no percurso por padrão, na ordem em que aparecem no código-fonte. Todo o resto é pulado, a menos que um atributo tabindex diga o contrário.

Os três significados de tabindex

ValorEfeitoQuando usar
tabindex="0"Acrescenta o elemento ao percurso na posição natural deleControles personalizados que não podem ser um <button> nativo
tabindex="-1"Tira o elemento do percurso; scripts ainda podem dar foco a eleAbas inativas, itens de menu acessados pelas setas, slides de carrossel fora da tela
tabindex="1" ou maisColoca o elemento antes de todo o resto, do menor número para o maiorQuase nunca. O foco fica pulando pela tela

O percurso segue essas regras à risca: valores positivos primeiro, em ordem crescente, depois a ordem do código-fonte. Um grupo de botões de opção com o mesmo name conta como uma única parada, já que as setas se movem dentro do grupo. Controles desativados, elementos <dialog> fechados, áreas inert ou hidden e o conteúdo de <details> fechados ficam de fora.

O que a verificação aponta

Elementos clicáveis que o teclado não alcança
Um <div onclick> ou um elemento com role="button" e sem tabindex. Quem usa mouse consegue clicar, e quem usa teclado nem chega até ele. Troque por <button type="button">, que traz foco, Enter e Espaço de graça.
Links sem href
Para o navegador, um <a> sem destino é texto comum, e o Tab passa direto por ele.
Controles com tabindex="-1"
Apontados, a menos que o link repita outro link alcançável para o mesmo endereço, ou faça parte de uma lista de abas, menu, barra de ferramentas ou grupo parecido em que as setas assumem o controle.
Link de pular
A verificação procura nas três primeiras paradas um link para uma âncora da mesma página, como <a href="#content">Skip to content</a>, e confirma que o id de destino existe. Também conta quantas teclas são precisas para ir do topo da página até o <main>. Com cinco ou menos, a falta de link de pular só é registrada a título de informação.
Contorno de foco
Procuramos nos blocos <style> da página, nos estilos inline e em até cinco folhas de estilo do mesmo site regras como a:focus { outline: none } ou * { outline: 0 }. Se nada substitui o contorno com estilos :focus-visible ou :focus, o resultado é vermelho, porque quem usa teclado não consegue ver onde está.
Outras paradas que merecem atenção
Elementos focáveis dentro de aria-hidden="true", paradas sem nome acessível, frames sem title e links para # ou javascript: que fazem papel de botão.

Trazer de volta um foco visível

Designers muitas vezes removem o contorno porque ele aparece depois de um clique do mouse e acham feio. O seletor :focus-visible resolve a discussão. Ele só se aplica quando o navegador julga que o usuário precisa ver o foco, o que na prática significa uso do teclado.

:focus-visible {
  outline: 3px solid #1a5fb4;
  outline-offset: 2px;
}

As WCAG 2.2 pedem um indicador com contraste de pelo menos 3:1 em relação às cores ao redor. O padrão :focus:not(:focus-visible) { outline: none } é seguro e não é apontado.

Limites de uma leitura estática

O percurso é calculado a partir do HTML que o nosso servidor recebe. Manipuladores de clique ligados em JavaScript com addEventListener não deixam rastro na marcação, então um <div> ligado desse jeito passa despercebido. Menus montados por script também ficam invisíveis aqui, assim como as armadilhas de teclado, em que o foco entra num widget e não consegue sair. Folhas de estilo de outro domínio, ou além da quinta, não são lidas.

Termine com o teste de verdade, que leva dois minutos. Deixe o mouse de lado, carregue a página e pressione Tab até chegar ao rodapé. Você deve sempre ver onde está, e deve conseguir abrir todos os menus e enviar todos os formulários.

Perguntas frequentes

Como testar a navegação por teclado eu mesmo?

Clique na barra de endereço e pressione Tab várias vezes. Shift+Tab volta, Enter funciona em links, Espaço em botões e caixas de seleção, as setas dentro de grupos de opções e menus, e Esc fecha caixas de diálogo. No Safari do macOS, ative antes a opção “Pressionar Tab para realçar cada item de uma página web” nos ajustes Avançados.

tabindex="0" num div basta para transformá-lo em botão?

Não. Torna o elemento focável, mas Enter e Espaço não fazem nada até você acrescentar manipuladores de teclas, e um leitor de tela não vai chamá-lo de botão sem role="button". Um <button> nativo dá as três coisas.

Preciso de um link de pular se a página tem um elemento main?

Sim, para quem enxerga e usa o teclado. Os leitores de tela conseguem pular para a região <main> com um atalho. Quem usa só a tecla Tab não tem esse atalho e precisa passar por cada item do menu, a menos que você ofereça um link de pular.

Por que a ordem do Tab não bate com o que vejo na tela?

O Tab segue a ordem do código-fonte HTML, não o layout visual. CSS como order, flex-direction: row-reverse, posicionamento em grid ou posicionamento absoluto pode mover elementos na tela sem mudar o lugar deles no código-fonte. Um tabindex positivo tem o mesmo efeito.

outline: none é sempre uma falha de acessibilidade?

Só quando nada o substitui. Uma regra que remove o contorno e define, no mesmo bloco, uma borda, um fundo ou uma sombra visíveis no foco está certa, e esta verificação conta isso como substituição.