Como usar seta para cima e para baixo em interfaces e código
A navegação por setas é uma das coisas mais subestimadas em UX e desenvolvimento. A maioria dos programadores implementa botões e esquece que o teclado já está lá. Seta para cima e para baixo não é apenas um detalhe visual — é uma via de acesso direta que usuários experientes cobram quando falta. No dia a dia, eu vejo gente reclamando de formulários que só respondem a cliques, menus que travam sem feedback visual, ou campos de input que não sabem para onde ir quando você aperta as setas. O problema geralmente não é a lógica em si, mas a ausência de handlers adequados.
Implementando seta para cima e para baixo no JavaScript
A base é simples. Você ouve o evento keydown e verifica a tecla pressionada. Aqui vai um exemplo funcional que eu uso em projetos pequenos: document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowUp') console.log('cima');
else if (e.key === 'ArrowDown') console.log('baixo');
});
Isso funciona para capturar a ação. Mas capturar não é o mesmo que implementar corretamente. O detalhe que as pessoas perdem é o preventDefault(). Se você não chamar isso em inputs de texto comuns, o navegador vai rolar a página inteira para cima ou para baixo, e não o que você queria. Coloca um e.preventDefault() quando for mover o foco entre elementos. Um problema real que eu encontrei recentemente envolveu uma lista de seleção dentro de um modal. Quando o usuário usava a seta para cima e para baixo, o scroll do modal competia com a navegação do item selecionado. A solução foi travar o scroll do body enquanto o modal estava aberto e limitar o movimento da seta aos itens visíveis na viewport, usando getBoundingClientRect() para checar se o elemento alvo ainda era visível antes de mover o foco.
Contextos práticos de uso
Existem três cenários onde seta para cima e para baixo faz diferença real: Listas e menus dropdown: A navegação padrão é exatamente essa — subir e descer entre opções. O importante aqui é o ciclo: quando chega no último item e aperta para baixo, volta para o primeiro. O mesmo ao contrário. Isso evita que o usuário fique preso num extremo.
Controles de volume ou intensidade: Muitas interfaces de áudio ou configurações usam setas como controle de magnitude. Setas para cima aumenta, setas para baixo diminui. O detalhe que quase todo mundo esquece é o acelerador progressivo. Segurar a tecla por 3 segundos deveria aumentar o step de 1 para 5 ou 10. Sem isso, ajustar volume de 0 a 100 manualmente é doloroso. Scroll em containers com overflow: Se você tem um painel com rolagem interna e quer controlar com as setas, o handle é direto — alterar scrollTop do container. O problema é a velocidade. O scroll nativo do navegador não leva em conta a resolução da tela. Um jump fixo de 30 pixels funciona em telas pequenas mas é insuficiente em monitores grandes. Calcule o delta proporcional à altura visível do container.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Acessibilidade e o padrão ARIA
Se você está construindo algo que usa seta para cima e para baixo, precisa olhar para os atributos ARIA. O atributo role="listbox" combinado com aria-selected e tabindex faz com que leitores de tela entendam o comportamento. Sem isso, seu componente é invisível para quem depende de navegação por teclado. O padrão WAI-ARIA recomenda especificamente que setas movimentem o foco dentro de grupos de itens relacionados. Seta para cima move para o item anterior, seta para baixo para o próximo. Não misture com atalhos de comando — isso gera conflito cognitivo.
Framework-agnostic versus bibliotecas prontas
Libs como React-Select, Vue-Select ou até mesmo o @floating-ui já trazem esse comportamento implementado. O problema é que elas escondem a complexidade. Quando algo dá errado — e dá —, você passa horas debugando porque o comportamento esperado não bate com o real. Na minha experiência, implementações próprias são melhores quando o componente é simples e o escopo é restrito. Se a lista tem menos de 50 itens e não precisa de virtualização, um handler customizado de 30 linhas resolve e dá controle total. Se a lista tem centenas ou milhares de itens, aí sim você precisa de uma biblioteca com suporte a lazy rendering.
Outro detalhe prático: seta para cima e para baixo em tabelas paginadas. Muita gente tenta mapear a seta para navegar entre páginas. Isso é errado. A seta deve mover entre linhas da página atual. A paginação deve ser controlada por outros meios, como botões ou teclas específicas (page up / page down). Confundir os dois gera confusão constante no usuário.
Limitações que ninguém menciona
Seta para cima e para baixo não funciona bem em dispositivos touch. Você pode adicionar um fallback com swipe, mas swipe vertical para cima significa "anterior" e para baixo significa "próximo" — isso é contra-intuitivo para quem está acostumado com scroll natural. A menos que seu público seja majoritariamente mobile, o custo-benefício do swipe nem sempre compensa. Também não é universal em todos os navegadores. No Firefox, o evento key pode retornar valores diferentes dependendo da localização do teclado. Eu já vi casos onde ArrowUp vinha como string vazia em teclados abreviados. A correção é usar tanto e.key quanto e.keyCode como fallback — 38 para cima e 40 para baixo.
Finalmente, existem situações onde seta para cima e para baixo simplesmente não deve existir. Em campos de busca com autocompletar, por exemplo, a navegação por setas pode conflitar com a intenção do usuário de digitar. O ideal é habilitar a navegação por setas apenas quando há resultados visíveis, e voltar ao modo de digitação normal assim que o usuário começar a typescript novamente.