O JavaScript transformou documentos estáticos em software. Aplicativos de página única parecem instantâneos. Sem recarregamentos de página inteira, sem telas brancas piscando. Mas essa velocidade tem um custo que muitas equipes ignoram: a maquinaria básica da web começa a apodrecer. A navegação torna-se frágil. Motores de busca têm dificuldade em seguir caminhos. Leitores de tela se perdem. E os usuários se veem presos em interfaces que parecem sites, mas se comportam como aplicativos de desktop quebrados.
O culpado geralmente é uma div com um manipulador onClick.
Pare de Usar Divs como Links
Uma div não possui significado semântico. É uma caixa. Quando você acopla um manipulador de clique a ela e a usa para rotear usuários para uma nova visualização, você está pedindo ao navegador que trate uma caixa de papelão como uma porta. O navegador recusa. Assim como todas as ferramentas construídas em torno do navegador.
Leitores de tela não anunciam uma div como um link ou um botão. Eles a ignoram ou a leem como texto simples. Um usuário que navega por voz não consegue selecioná-la. Um rastreador de motor de busca, escaneando sua página em busca de URLs descobertas, não vê nada para seguir. Sua rota pode muito bem não existir.
Pior ainda, você perde os comportamentos que os usuários já conhecem. Um link real permite que alguém clique com o botão direito para abrir em uma nova aba, adicione o destino aos favoritos ou copie o endereço para compartilhar. Usuários de teclado esperam pressionar Tab para alcançá-lo e Enter para abri-lo. Uma div não oferece nada disso. Mesmo que você adicione tabIndex, role="link" e ouvintes de teclado, você estará reconstruindo, de forma precária, o que o navegador oferece gratuitamente. E você esquecerá um caso de borda. Você sempre esquece.
Use Âncoras para Destinos, Botões para Ações
O HTML já resolveu isso. A confusão começa porque ambos os elementos parecem clicáveis, então os desenvolvedores os tratam como intercambiáveis. Eles não são.
Use uma tag <a> quando quiser mover o usuário para uma nova URL. Não uma troca de visualização simulada, nem uma mudança de estado, mas um local real. O atributo href deve conter um endereço real:
<a href="/docs">Documentation</a>
É isso. Se o usuário vai para algum lugar, use um link.
Use um <button> quando algo acontecer na página atual. Botões são para ações como:
- Abrir um modal
- Enviar um formulário
- Salvar configurações
- Alternar um menu
Links são para destinos. Botões são para ações. Misturar os dois confunde sua interface e quebra as expectativas do usuário.
Deixe o Navegador Fazer o Trabalho Dele
Os navegadores modernos são o resultado de décadas de evolução e padronização. Eles lidam com segurança, histórico, prefetching e acessibilidade melhor do que qualquer JavaScript escrito manualmente jamais conseguirá.
Uma tag de âncora genuína alimenta automaticamente a pilha de histórico do navegador. Ela funciona com o menu de contexto nativo. Ela participa dos algoritmos de prefetching integrados do navegador quando o usuário passa o mouse ou foca nela, fazendo com que seu aplicativo pareça mais rápido sem que você escreva uma única linha de código. Ela respeita as preferências do usuário para abrir links. Ela coopera com gerenciadores de senhas, ferramentas de tradução e modos de leitura.
Quando você substitui isso por uma função de navegação em JavaScript, você abre mão de tudo isso. Você não está apenas perdendo recursos; você está forçando os usuários a abandonar hábitos que eles construíram em todos os outros sites da internet. Isso não é uma decisão técnica. É uma experiência de usuário hostil.
Verifique o que seu Framework Realmente Renderiza
React Router, Vue Router, componentes Link do Next.js, SvelteKit. Essas ferramentas fazem o roteamento no lado do cliente parecer sem esforço. Mas a abstração gera erros.
Inspecione seu DOM. Abra as ferramentas de desenvolvedor do navegador e observe os elementos que seu framework emite. Um componente <Link> deve ser renderizado como uma tag <a> real com um atributo href válido no HTML final. Se ele for renderizado como um span, uma div ou qualquer outra coisa sem um href adequado, sua abstração falhou com você. Corrija o componente. Sobrescreva o padrão. Use a prop passHref ou o equivalente do framework. Não confie que o framework fará o certo sem verificação.
Isso também é importante para incompatibilidades de hidratação (hydration mismatches). Se o servidor renderiza um link e o cliente o hidrata como um não-link, você cria bugs de acessibilidade difíceis de rastrear, porque o HTML parece correto em seu código-fonte, mas está errado no DOM em tempo de execução.
Proíba Destinos Falsos
Existe um padrão que se recusa a morrer: href="javascript:void(0)". Desenvolvedores o utilizam quando querem algo que pareça um link, mas que funcione como um botão, geralmente porque não querem estilizar um botão ou porque uma base de código mais antiga exige isso.
Stop. This is not a URL. It gives the browser no destination. It pollutes the history stack with unusable states. It breaks browser history and accessibility. It is a trap. If you need click behavior without navigation, you need a <button>. Style it to look however you want. CSS does not care whether the element is a button or a link. Your users do.
Write Text That Explains Where the User Is Going
The words inside your link matter. Screen reader users often pull up a list of every link on the page to scan quickly. If your links all say "Read more" or "Click here," that list becomes useless noise.
Be specific. Compare these:
- Bad:
<a href="/security/api-guide">Read more</a> - Good:
<a href="/security/api-guide">Read the API security guide</a>
The second tells the user exactly what they will find. It gives search engines context about the destination page. It makes your link list navigable. Descriptive link text is one of the cheapest accessibility wins you can earn.
Test Like You Mean It
Architecture means nothing if you do not verify it.
First, test your keyboard flow. Unplug your mouse. Tab through every interactive element on your site. Every genuine link must show a visible focus outline, not a subtle glow that disappears against your background, but a clear ring that a tired eye can spot. Press Enter. It must activate the link. If Tab skips an element, or if Enter does nothing, you have a bug.
Second, test your routes at the server level. Client-side routing is a thin veneer. If a user bookmarks /dashboard/reports and returns tomorrow, or hits refresh, your server must know how to serve that page. Configure your reverse proxy or your server framework to fall back to your application shell for unknown paths, or serve the correct HTML directly. A dead 404 on refresh is not a minor bug. It is a broken promise.
JavaScript is a powerful layer
