Programação Por Equipe DevBras

Capa do artigo: Botão ou link? Botão faz uma ação, link leva a um lugar

Botão ou link? Botão faz uma ação, link leva a um lugar

Escolha entre a e button pelo que o elemento faz, troque divs clicáveis e href="#" pelo elemento certo e teste com Tab, Enter, Espaço e clique do meio.

O "Entrar" do cabeçalho é uma div com onclick. O Tab passa direto por ela, o leitor de tela anuncia só o texto, sem dizer que é clicável, e Enter não faz nada. Ao lado, o menu abre com um <a href="#"> que joga a página para o topo quando o script demora a carregar. E o "Ver planos" é um botão que muda o location por JavaScript, então ninguém consegue abri-lo numa aba nova. A resposta curta: se leva para outra página ou outro ponto da página, é <a href>; se faz alguma coisa ali mesmo, é <button>. O visual não decide nada: um link pode parecer botão e vice-versa, com CSS.

Escolhido o elemento certo, o navegador entrega de graça o foco, as teclas, o anúncio para tecnologia assistiva e comportamentos que as pessoas esperam, como abrir em nova aba.

O que cada elemento traz pronto

Um link com href recebe foco com Tab, é ativado com Enter, é anunciado como "link", aparece na lista de links do leitor de tela, pode ser aberto em nova aba com clique do meio ou Ctrl + clique, tem "copiar endereço do link" no menu de contexto, entra no histórico e é seguido por buscadores.

Um botão recebe foco com Tab, é ativado com Enter e com Espaço, é anunciado como "botão", aceita o atributo disabled (que tira o foco e bloqueia a ação) e, dentro de um formulário, envia o formulário por padrão.

Uma div com clique não traz nada disso. Para chegar perto de um botão, ela precisa de role="button", tabindex="0", tratamento de Enter e Espaço no teclado, estilo de foco e lógica própria de desabilitado. É reimplementar, com bugs, o que o <button> já faz.

A troca no sentido contrário também cobra. Um botão que navega por JavaScript perde nova aba, copiar endereço e indexação. Um link sem destino real, usado para abrir menu, é anunciado como link, faz a pessoa esperar uma navegação que não vem e, com href="#", rola a página para o topo se o script falhar.

Como escolher e corrigir

  1. Pergunte o que acontece ao ativar. A URL muda ou a pessoa vai para outro trecho? Link. Algo muda na própria tela, um envio acontece, um painel abre? Botão.
  2. Troque toda div e span clicáveis por <button type="button"> ou <a href>.
  3. Troque href="#" e href="javascript:void(0)" por botões, quando não há destino.
  4. Troque botões que mudam o location por links com o endereço real.
  5. Declare type="button" em todo botão dentro de formulário que não deve enviá-lo.
  6. Dê nome visível ao elemento, o mesmo que será anunciado. Se for só ícone, veja ícone no botão precisa de nome.
  7. Ajuste o visual com CSS depois, sem mexer na escolha do elemento.

O cabeçalho antes

<div class="btn" onclick="abrirLogin()">Entrar</div>
<a href="#" onclick="alternarMenu()">Menu</a>
<button onclick="location.href='/planos'">Ver planos</button>

O cabeçalho depois

<a class="btn" href="/entrar">Entrar</a>

<button type="button" aria-expanded="false" aria-controls="menu-principal">Menu</button>
<ul id="menu-principal" hidden>
  <li><a href="/blog">Blog</a></li>
  <li><a href="/contato">Contato</a></li>
</ul>

<a class="btn" href="/planos">Ver planos</a>
const botaoMenu = document.querySelector('[aria-controls="menu-principal"]');
const menu = document.getElementById('menu-principal');

botaoMenu.addEventListener('click', () => {
  const aberto = botaoMenu.getAttribute('aria-expanded') === 'true';
  botaoMenu.setAttribute('aria-expanded', String(!aberto));
  menu.hidden = aberto;
});

"Entrar" virou link porque leva à página de login, que tem endereço próprio. Se o login abrisse num diálogo na mesma página, seria botão. O menu virou botão com aria-expanded, que diz ao leitor de tela se ele está aberto ou fechado. O evento click do botão dispara com mouse, Enter e Espaço, então não há tratamento de teclado para escrever. "Ver planos" virou link, e agora abre em nova aba, aparece para buscadores e funciona mesmo se o JavaScript falhar.

Casos que confundem

  • "Baixar relatório": se é um arquivo com endereço, link (com o atributo download, se quiser). Se o arquivo é gerado por uma ação que precisa de POST, botão num formulário.
  • Abas: trocar o painel visível sem mudar de página é ação, então são botões, com o padrão de abas da ARIA. Se cada aba tem URL própria, é navegação com links.
  • "Excluir" numa lista: sempre botão, nunca link com GET. Link é seguido por pré-carregamento e robôs; uma exclusão por GET pode acontecer sem ninguém clicar.
  • Componentes de rota em React, Vue ou Angular: o componente de link do roteador gera um <a href> de verdade. Use-o para navegar, em vez de um botão que chama a navegação por código.

Os eventos de ponteiro e teclado por trás disso estão em o clique no ponteiro não cobre o teclado, e o caso de bibliotecas que entregam o visual sem a semântica, em componente pronto não é acessível por padrão.

Erros comuns

  • div ou span com onclick. Não recebe foco, não responde a teclado e não é anunciada como controle.
  • Link com href="#" para abrir menu. Anunciado como link, rola para o topo quando o script falha e polui o histórico.
  • Botão que navega por JavaScript. Sem nova aba, sem copiar endereço, sem indexação, sem funcionar quando o script quebra.
  • Botão sem type dentro de formulário. O padrão é submit: o "Mostrar senha" envia o formulário.
  • Exclusão por link com GET. Pré-carregamento, robôs e extensões podem disparar a ação.
  • Escolher pelo visual. "Parece botão" não é critério; o critério é o que acontece ao ativar.

Checklist

  • Nenhuma div ou span tem manipulador de clique como único jeito de acionar algo.
  • Todo link tem href com destino real.
  • Toda navegação pode ser aberta em nova aba com clique do meio.
  • Todo botão dentro de formulário tem type declarado.
  • Botões que abrem e fecham coisas usam aria-expanded.
  • Ações destrutivas são botões, nunca links com GET.
  • Tudo funciona com Tab, Enter e, nos botões, Espaço.

Desconecte o mouse e percorra o cabeçalho e o rodapé do seu site só com Tab, Enter e Espaço: algum controle foi pulado, ou exigiu um script extra só para receber foco?

Avalie este artigo

Referência: https://www.w3.org/WAI/ARIA/apg/patterns/button/