Abas parecem simples porque exibem um painel e escondem os outros. A complexidade está no foco. Uma implementação feita só com botões lado a lado costuma obrigar quem usa teclado a pressionar Tab em todas as opções. Outra pode mudar o conteúdo ao receber foco, mesmo que o painel ainda esteja carregando. Há também abas visualmente ativas que continuam com aria-selected="false".

Uma interface de abas acessível sincroniza quatro coisas: foco, seleção, painel visível e conteúdo anunciado. O conjunto precisa ter uma entrada previsível na ordem da página, responder às setas corretas e continuar compreensível quando o layout estreita. Este guia mostra quando usar o padrão, como montar sua estrutura e como testar a URL publicada.

Use abas apenas para conteúdo paralelo

Abas funcionam quando vários painéis ocupam o mesmo lugar e representam alternativas equivalentes dentro de um contexto. Exemplos incluem detalhes de um produto, demonstrações por linguagem ou visões resumidas do mesmo conjunto de dados.

Escolha outro formato quando:

  • a pessoa precisa comparar os painéis ao mesmo tempo;
  • o conteúdo deve ser lido em sequência;
  • cada seção merece URL, título e compartilhamento próprios;
  • existem tantas opções que os rótulos não cabem sem rolagem difícil;
  • carregar um painel pode demorar;
  • o conteúdo oculto é essencial para entender a oferta.

Uma lista, grade ou sequência de seções costuma funcionar melhor para comparação e leitura contínua. Um acordeão mantém todos os cabeçalhos na ordem normal de Tab. Abas usam navegação interna com setas e devem ter apenas um item do conjunto na sequência de Tab.

Não use details para imitar abas. O HTML Standard trata details como disclosure, não como tab widget. O comportamento de teclado e a relação entre controles e painéis são diferentes.

Modele tablist, tab e tabpanel juntos

O padrão de abas da W3C define três funções:

  • tablist agrupa as abas;
  • tab representa cada opção;
  • tabpanel contém o conteúdo associado.

Uma estrutura básica pode começar assim:

<div role="tablist" aria-label="Detalhes do plano">
<button
type="button"
id="aba-recursos"
role="tab"
aria-selected="true"
aria-controls="painel-recursos"
tabindex="0"
>
Recursos
</button>
<button
type="button"
id="aba-limites"
role="tab"
aria-selected="false"
aria-controls="painel-limites"
tabindex="-1"
>
Limites
</button>
</div>
<div id="painel-recursos" role="tabpanel" aria-labelledby="aba-recursos">...</div>
<div id="painel-limites" role="tabpanel" aria-labelledby="aba-limites" hidden>...</div>

O tablist precisa de nome quando o contexto não fica claro. Prefira aria-labelledby se já existe um título visível; use aria-label para um rótulo curto quando não houver título adequado.

Cada tab aponta para um único painel com aria-controls. Cada tabpanel aponta de volta para sua aba com aria-labelledby. IDs duplicados ou referências quebradas desmontam essa relação mesmo que a interface continue parecendo correta.

Separe foco de seleção

Foco e seleção podem coincidir, mas não significam a mesma coisa. Foco identifica qual aba recebe o próximo comando de teclado. Seleção identifica qual painel está ativo.

Use roving tabindex para manter uma única aba na sequência da página:

  • a aba ativa ou o destino definido pelo produto usa tabindex="0";
  • as outras usam tabindex="-1";
  • a seta muda o tabindex anterior para -1;
  • a seta muda o próximo item para 0 e chama focus();
  • Tab sai do conjunto em vez de visitar cada aba.

Não use valores positivos de tabindex. Eles criam uma ordem paralela ao DOM e ficam frágeis quando o conteúdo muda. A orientação da W3C sobre interfaces de teclado recomenda alinhar ordem de foco, ordem do DOM e leitura sempre que possível.

Escolha ativação automática ou manual

Na ativação automática, mover foco com a seta também seleciona a aba e exibe seu painel. Esse comportamento funciona quando o conteúdo já está disponível e a troca não tem latência perceptível.

Na ativação manual, as setas movem apenas o foco. Enter ou Espaço confirma a seleção. Prefira esse modelo quando o painel depende de rede, renderização pesada ou qualquer atraso que torne a passagem entre abas lenta.

Não misture os dois modelos. Se a primeira seta apenas foca e a segunda troca conteúdo, a interação fica impossível de prever. Documente a escolha no componente e cubra o ciclo completo em teste.

Ativar uma aba ao receber foco não deve enviar formulário, abrir página, deslocar foco para outro elemento nem iniciar uma tarefa irreversível. A seleção troca o painel do próprio componente.

Implemente as teclas conforme a orientação

Num conjunto horizontal:

  • Seta para a direita move para a próxima aba;
  • Seta para a esquerda move para a anterior;
  • o movimento pode circular entre o fim e o início;
  • Home pode levar à primeira aba;
  • End pode levar à última;
  • Enter e Espaço ativam no modelo manual;
  • Tab sai do tablist em direção ao painel ou ao próximo controle lógico.

Não intercepte Seta para cima e Seta para baixo num conjunto horizontal. O navegador precisa dessas teclas para rolar a página. Em um tablist vertical, defina aria-orientation="vertical" e use setas para cima e para baixo.

Se o painel ativo não contém nenhum elemento focável, tabindex="0" no tabpanel pode incluí-lo na sequência. Se o primeiro conteúdo significativo já é um link, botão ou campo, evite criar uma parada redundante apenas no contêiner.

O roteiro de navegação por teclado ajuda a conferir foco visível, ordem e comportamento de teclas em diferentes sistemas.

Sincronize todos os estados numa única operação

Ao selecionar uma aba, atualize o conjunto inteiro:

  1. defina aria-selected="false" e tabindex="-1" na aba anterior;
  2. esconda o painel anterior;
  3. defina aria-selected="true" e tabindex="0" na nova aba;
  4. revele o painel correspondente;
  5. mantenha o foco na aba que recebeu a ação, salvo um fluxo explicitamente definido.

Não use apenas uma classe como .active. Também não deixe dois itens selecionados ou dois painéis visíveis num padrão de seleção única. Se o estado vem de um framework, derive atributos, estilo e visibilidade da mesma fonte para reduzir desencontros.

Painéis ocultos precisam sair da apresentação visual, da árvore de acessibilidade e da ordem de foco. O atributo hidden atende ao caso simples. opacity: 0, deslocamento fora da tela ou altura zero podem deixar links ocultos alcançáveis.

O artigo sobre nomes acessíveis de links e botões ajuda a revisar rótulos repetidos, ícones e nomes que não correspondem ao texto visível.

Não transforme a troca em anúncio excessivo

O leitor de tela recebe mudanças de foco, seleção e relação com o painel. Evite adicionar aria-live ao conjunto inteiro por padrão. Uma região ao vivo pode reler conteúdo extenso a cada seta e interromper o que a pessoa já estava ouvindo.

Use rótulos curtos que diferenciem as opções. “Visão geral”, “Recursos” e “Limites” dão mais contexto do que “Aba 1”, “Aba 2” e “Aba 3”. Se os rótulos dependem de um título externo, confira a sequência anunciada no leitor de tela real.

Teste também o painel. Títulos, listas, tabelas e controles internos continuam precisando de semântica própria. tabpanel não descreve automaticamente todo o conteúdo.

Planeje URL, histórico e carregamento

Algumas abas representam um estado que merece link direto. Nesse caso, defina como a URL identifica a seleção, como o botão Voltar se comporta e qual painel aparece quando o valor é inválido.

Não use um link com destino falso apenas para obter estilo de aba. Se a interação troca conteúdo na mesma página, um botão com role="tab" costuma ser a base mais previsível. Se cada opção navega para outra URL, talvez sejam links de navegação estilizados, não abas.

Carregar o conteúdo sob demanda exige estados de espera e erro. Na ativação automática, não bloqueie cada movimento de seta com uma requisição. Pré-carregue ou use ativação manual. Preserve o painel anterior até o novo estado estar pronto e não envie o foco para um spinner sem um motivo claro.

Trate rolagem e largura estreita

Uma fileira de abas que cabe no desktop pode cortar rótulos no celular. Evite reduzir fonte e alvo até tudo caber. Considere:

  • permitir quebra controlada quando a ordem continuar clara;
  • usar rolagem horizontal com indicação visível de conteúdo adicional;
  • trocar para uma lista de seções em larguras estreitas;
  • encurtar rótulos sem perder significado;
  • limitar o número de opções do conjunto.

Se o foco muda para uma aba fora da área visível, role o próprio conjunto apenas o suficiente para revelar o controle. Não mova a página de forma brusca. Confirme foco não encoberto por cabeçalhos fixos e teste zoom de 200% e 400%.

O guia de zoom e reflow ajuda a identificar corte, sobreposição e rolagem em duas dimensões. O roteiro de alvos de toque cobre tamanho e espaçamento entre rótulos próximos.

Teste o componente como uma máquina de estados

Uma captura prova apenas o estilo inicial. Monte uma matriz de estados:

Situação Evidência esperada
entrada com Tab foco chega à aba ativa ou ao destino definido
seta seguinte foco muda uma posição e respeita a orientação
ativação manual painel muda apenas após Enter ou Espaço
ativação automática painel aparece sem atraso perceptível
aba selecionada estilo, aria-selected, tabindex e painel concordam
painel oculto conteúdo não aparece, não é lido e não recebe foco
largura estreita rótulos e foco permanecem visíveis e operáveis
falha de carregamento há mensagem útil sem abandonar a posição do componente

Repita a matriz com mouse, toque, teclado e leitor de tela. O guia de teste com leitor de tela ajuda a conferir nome, função, estado e relação sem depender apenas da árvore de acessibilidade do navegador.

Use um pedido de correção verificável

Revise as interfaces de abas desta landing page sem publicar ainda.
Escopo:
- fonte e commit: [referência]
- rotas: [lista]
- conjuntos de abas: [inventário]
Para cada conjunto:
1. Confirme se abas são o padrão adequado para o conteúdo.
2. Registre tablist, tabs, tabpanels, orientação e estado inicial.
3. Escolha ativação automática apenas quando os painéis estiverem prontos sem atraso.
4. Implemente roving tabindex e as teclas esperadas para a orientação.
5. Sincronize foco, aria-selected, painel visível e estilo ativo.
6. Remova painéis ocultos da leitura e da ordem de foco.
7. Teste teclado, leitor de tela, toque, zoom, URL e estados de erro.
8. Preserve conteúdo, analytics e lógica de negócio.
Entregue a matriz de estados, o diff e as evidências antes de publicar.

O pedido impede uma correção apenas visual. Cada mudança precisa aparecer no HTML, no teclado e na tecnologia assistiva.

Reteste a versão publicada

Depois da aprovação, siga a documentação de Publicação: publique uma nova Version, acompanhe o deploy e abra a URL final. Teste entrada, setas, seleção, saída com Tab, links internos e comportamento do histórico.

Faça o percurso depois que fontes, scripts e dados terminarem de carregar. Se a hidratação redefinir a seleção, esconder o foco ou tornar painéis inativos alcançáveis, corrija a fonte e republique o mesmo Site. As abas estão prontas quando foco, seleção e conteúdo permanecem sincronizados em toda forma de entrada.