Um campo de cidade, produto ou profissão pode começar como um input simples e terminar como um componente que ninguém consegue operar sem mouse. A lista abre, mas o foco some. As setas movem a página. O leitor de tela não anuncia quantas sugestões existem. Pressionar Esc apaga o texto em vez de fechar a lista.

Esse tipo de falha aparece quando uma landing page criada por IA copia a aparência de um autocomplete sem implementar o contrato do componente. O resultado parece pronto numa captura e quebra justamente durante o preenchimento.

Este guia mostra como escolher o controle mais simples, implementar um combobox quando ele for necessário e testar o fluxo na versão publicada.

Decida se você precisa de um combobox

Combobox é um campo com um popup associado. A pessoa pode digitar, selecionar uma opção ou fazer as duas coisas, conforme a regra do produto. O popup costuma ser uma lista, mas também pode ser uma grade, árvore ou diálogo.

Antes de escrever ARIA, classifique o dado:

Necessidade Controle inicial
poucas opções fixas <select> ou radios
várias opções fixas, sem busca <select>
sugestões opcionais e valor livre input com sugestões
busca e seleção em uma lista grande combobox editável
escolha única com conteúdo rico combobox ou diálogo, após teste

Um <select> nativo já oferece nome, valor, foco e interação adaptada ao navegador. Trocar esse controle por um popup personalizado apenas para mudar o visual cria trabalho de teclado, foco e anúncio que o projeto precisa manter.

O elemento <datalist> pode oferecer sugestões com pouco código, mas não substitui todos os demais controles. A documentação do MDN sobre datalist registra diferenças de navegador, limitações de zoom, pouco controle em alto contraste e combinações que não anunciam as sugestões. Use-o apenas depois de testar os navegadores e tecnologias assistivas do público.

Defina o comportamento antes dos atributos

Escreva o contrato do campo em linguagem direta:

Rótulo: Cidade de atendimento
Valor aceito: somente uma cidade cadastrada
Quando abrir: após 2 caracteres ou ao usar a seta para baixo
Como filtrar: início ou trecho do nome
Sem resultados: manter o texto e informar que não há correspondência
Escape: fechar a lista sem apagar o valor anterior
Enter: confirmar a opção em foco
Tab: sair do campo com o valor já confirmado

A diferença entre sugestão e valor permitido precisa estar clara. Se a pessoa pode informar qualquer profissão, a lista ajuda, mas não limita. Se a entrega atende somente cidades cadastradas, o formulário deve rejeitar um texto que não corresponde a uma opção válida.

Não use a presença na lista como validação implícita. Registre a regra no código e mostre uma mensagem compreensível quando o valor não for aceito.

Dê nome ao campo e relacione o popup

O padrão de combobox da W3C descreve um controle recolhido por padrão e um popup associado. Num combobox editável, um esqueleto comum é:

<label for="cidade">Cidade de atendimento</label>
<input
id="cidade"
name="cidade"
type="text"
role="combobox"
aria-autocomplete="list"
aria-expanded="false"
aria-controls="opcoes-cidade"
aria-activedescendant=""
/>
<ul id="opcoes-cidade" role="listbox" hidden>
<li id="cidade-recife" role="option">Recife</li>
<li id="cidade-salvador" role="option">Salvador</li>
</ul>

Esse HTML não cria a interação. ARIA expõe nome, função, relação e estado, mas o JavaScript ainda precisa abrir a lista, atualizar opções, mover a opção ativa e confirmar a escolha.

Mantenha as relações sincronizadas:

  • aria-expanded acompanha a visibilidade real do popup;
  • aria-controls aponta para o id do popup;
  • aria-autocomplete descreve o comportamento oferecido;
  • aria-activedescendant aponta para a opção ativa quando o foco DOM permanece no input;
  • cada opção selecionada usa aria-selected="true" no momento correto.

O guia do MDN para o papel combobox reforça que todo combobox precisa de nome acessível. Prefira um <label> visível associado ao input. Não combine label, aria-label e aria-labelledby sem necessidade.

Preserve a edição de texto

Num combobox editável, o teclado serve a duas tarefas ao mesmo tempo. A pessoa edita texto e navega pelas sugestões. Não intercepte teclas que pertencem ao input.

O comportamento esperado costuma incluir:

  • Seta para baixo abre a lista ou move para a próxima opção;
  • Seta para cima move para a opção anterior;
  • Enter confirma a opção ativa;
  • Esc fecha o popup sem destruir a entrada anterior;
  • Home e End continuam editando o texto quando o padrão adotado reserva essas teclas ao input;
  • caracteres digitados continuam alterando a consulta;
  • Tab segue para o próximo controle da página.

Não coloque todas as opções na ordem de Tab. O campo deve ser uma parada na página. A navegação interna acontece com as teclas definidas para o widget.

Se o foco DOM permanecer no input, atualize aria-activedescendant conforme a pessoa percorre a lista. A opção ativa também precisa de um indicador visual que continue perceptível em alto contraste e com zoom.

O artigo sobre navegação por teclado ajuda a testar a sequência antes e depois do componente.

Separe foco, seleção e valor

Três estados podem coexistir:

  • o input mantém o foco DOM;
  • uma opção está ativa para navegação;
  • outra opção representa o valor confirmado anteriormente.

Misturar esses estados causa alterações acidentais. A pessoa percorre as sugestões para conhecer a lista, pressiona Esc e descobre que o campo mudou.

Atualize o valor confirmado apenas na ação definida pelo contrato, como Enter ou clique. Enquanto a pessoa apenas navega, mantenha o valor anterior recuperável.

Quando o filtro mudar, confirme se a opção ativa ainda existe. Nunca deixe aria-activedescendant apontar para um elemento removido do DOM.

Comunique carregamento, quantidade e ausência de resultados

Sugestões remotas trazem estados que não aparecem numa lista estática:

  1. consulta aguardando;
  2. resultados carregados;
  3. nenhuma correspondência;
  4. falha de rede;
  5. resposta antiga chegando depois da busca mais recente.

Não anuncie uma mensagem a cada tecla. Isso interrompe a leitura e cria ruído. Aguarde o filtro estabilizar, descarte respostas antigas e use um status curto quando a informação ajudar a concluir a tarefa.

Exemplos úteis:

5 cidades encontradas.
Nenhuma cidade encontrada. Revise a busca.
Não foi possível carregar as cidades. Tente novamente.

O guia sobre mensagens de erro e status acessíveis detalha quando usar uma região de status e como evitar anúncios repetidos.

Não trate “sem resultados” como erro do campo quando valores livres são permitidos. A mensagem precisa refletir a regra real.

Evite sugestões que mudam a página sozinhas

Selecionar uma sugestão não deve enviar o formulário, abrir uma nova rota ou mover o foco sem aviso. A WCAG sobre mudança de contexto durante a entrada pede previsibilidade quando a alteração de um controle provoca uma mudança relevante.

Em busca de conteúdo, permita confirmar a consulta num botão. Em escolha de cidade, atualize o campo e mantenha a pessoa no formulário. Se o produto realmente precisa navegar ao selecionar, explique o comportamento antes do controle e teste se a pessoa consegue voltar sem perder dados.

Teste além do caminho feliz

Monte uma matriz curta:

Cenário Verificação
lista fechada nome, função, valor e estado são anunciados
digitação texto permanece editável e o filtro acompanha a entrada
setas opção ativa muda sem rolar a página
Enter opção ativa vira o valor confirmado
Esc popup fecha e preserva o valor anterior
sem resultados mensagem aparece sem transformar texto livre em erro
carregamento lento resposta antiga não substitui a consulta atual
zoom e alto contraste lista, foco e opção ativa continuam visíveis
toque campo e opções têm alvos utilizáveis
leitor de tela nome, expansão, opção ativa e resultado são compreensíveis

Teste pelo menos teclado, toque e um leitor de tela disponível. Os exemplos da APG são referências de comportamento, não componentes prontos para produção. A própria W3C alerta para diferenças de suporte entre navegadores e tecnologias assistivas.

Use o roteiro de teste com leitor de tela para registrar navegador, leitor, versão e tarefa executada. Se a lista estiver correta apenas numa combinação, o componente ainda precisa de trabalho.

Peça uma revisão precisa ao agente

Um pedido útil informa a regra do dado e o comportamento esperado:

Revise o campo Cidade de atendimento desta landing page.
O valor deve corresponder a uma cidade cadastrada.
A lista abre após 2 caracteres e mostra no máximo 8 resultados.
Mantenha o foco no input e use setas para navegar pelas opções.
Enter confirma, Escape fecha sem apagar e Tab segue o formulário.
Verifique nome acessível, aria-expanded, aria-controls,
aria-activedescendant, seleção, estados assíncronos e foco visível.
Teste teclado, zoom, alto contraste, toque e leitor de tela.
Não publique antes de apresentar os resultados.

Esse nível de detalhe impede que o agente escolha sozinho se o campo aceita valor livre, quando consulta a API ou o que significa confirmar.

Reteste na versão publicada

Depois de corrigir o componente, gere a saída final e confira se os arquivos públicos preservam o código, os estilos e as mensagens. No Site no Ar, o agente trabalha nos arquivos em public/, chama deploy e acompanha deploy_status até um estado terminal, conforme o guia de Publicação.

Na URL publicada:

  1. repita a consulta com teclado;
  2. force uma resposta vazia e uma falha autorizada;
  3. confirme que o popup não fica cortado por contêiner ou header;
  4. teste em largura pequena e com zoom;
  5. verifique o valor que o formulário realmente envia;
  6. confirme que a versão nova está ativa antes de encerrar.

Um combobox acessível não é uma coleção de atributos. É um acordo estável entre texto, popup, foco, seleção e validação. Quando um controle nativo resolve, use-o. Quando o produto exige busca e sugestões, implemente o contrato inteiro e prove o resultado no ambiente que as pessoas vão usar.