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 atendimentoValor aceito: somente uma cidade cadastradaQuando abrir: após 2 caracteres ou ao usar a seta para baixoComo filtrar: início ou trecho do nomeSem resultados: manter o texto e informar que não há correspondênciaEscape: fechar a lista sem apagar o valor anteriorEnter: confirmar a opção em focoTab: sair do campo com o valor já confirmadoA 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-expandedacompanha a visibilidade real do popup;aria-controlsaponta para oiddo popup;aria-autocompletedescreve o comportamento oferecido;aria-activedescendantaponta 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 baixoabre a lista ou move para a próxima opção;Seta para cimamove para a opção anterior;Enterconfirma a opção ativa;Escfecha o popup sem destruir a entrada anterior;HomeeEndcontinuam editando o texto quando o padrão adotado reserva essas teclas ao input;- caracteres digitados continuam alterando a consulta;
Tabsegue 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:
- consulta aguardando;
- resultados carregados;
- nenhuma correspondência;
- falha de rede;
- 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:
- repita a consulta com teclado;
- force uma resposta vazia e uma falha autorizada;
- confirme que o popup não fica cortado por contêiner ou header;
- teste em largura pequena e com zoom;
- verifique o valor que o formulário realmente envia;
- 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.
