Quando um botão não é um botão?

Publicados: 2022-03-10
Resumo rápido ↬ Nem tudo que é redondo e se destaca é considerado botão. Neste artigo, Vadim explica como você pode criar um botão interativo adequado para seus usuários – um que não deve ser confundido com mais nada.

Digamos que você tenha uma parte de uma interface que o usuário clica e algo acontece. Soa como um botão para mim, mas vamos chamá-lo de “coisa de clique” por enquanto. Eu sei, você está confiante de que é um botão também: é arredondado e se destaca com uma bela cor de tomate, pedindo para interagir. Mas vamos pensar sobre isso por um momento. Vai economizar tempo a longo prazo, eu prometo.

Coisa parecida com um botão de tomate com o texto 'Algo' nele.
Design para sua 'coisa clicada' (visualização grande)

E se o texto dessa coisa clicada fosse “Leia mais” e clicar nele levasse o usuário a um artigo em outra página? Hmm. E se houver uma palavra sublinhada em azul, “Fechar”, que fecha a caixa de diálogo pop-up? É um link só porque está azul e sublinhado? Claro que não.

Link semelhante a um botão e botão semelhante a um link
O dilema do link ou botão (visualização grande)
Mais depois do salto! Continue lendo abaixo ↓

Uau! Parece que não há como saber se é um link ou um botão apenas olhando para ele. Isso é louco! Precisamos entender o que essa coisa faz antes de escolher o elemento certo. Mas e se ainda não soubermos o que ele faz ou estivermos simplesmente confusos? Bem, há um fluxograma útil para nós:

Fluxograma: é um botão. Se não, então é um link. É isso.
Um fluxograma científico para escolher o elemento certo (visualização grande)
  1. É um botão.
  2. Se não, então é um link.
  3. É isso.

Então, tudo é um botão? Não, mas você sempre pode começar com um botão para quase qualquer elemento que possa ser clicado ou interagir de maneira semelhante. E se estiver faltando algo, como navegação para outra página, use um link. E não, um ponteiro não é uma razão para torná-lo <a href> . Temos cursor: pointer para isso.

Botão de tomate em foco com o texto 'Algo' nele
Não se esqueça de fornecer estilos de foco. (Visualização grande)

Tudo bem, é um <button> — nós concordamos com isso. Vamos colocá-lo em nosso modelo e estilizá-lo de acordo com o design: alguns preenchimentos, arredondamentos, preenchimento de tomate, texto em branco e até alguns estilos de foco. Oh, isso é tão legal de sua parte.

 <button type="button" class="button"> Something </button> <style> .button { display: inline-block; padding: 10px 20px; border-radius: 20px; background-color: tomato; color: white; } .button:focus { outline: none; box-shadow: 0 0 0 5px #006AE3; } </style>

Isso não demorou muito. Você queria construí-lo rapidamente e almoçar porque está com fome. Ok, vamos ver como fica e vamos em frente.

Botão de tomate de aparência feia, renderizado em um navegador
Às vezes, o navegador não é seu melhor amigo. (Visualização grande)

Oh meu Deus! Algo está errado com o navegador. Por que esse botão é tão feio? O texto é minúsculo, embora tenhamos definido explicitamente o body para 16px e até mesmo a font-family está errada. A borda arredondada com uma pseudo-sombra boba é tão retrô que ainda nem é tendência.

Ahh, é o estilo padrão do navegador. Você precisa desfazê-lo com cuidado ou até mesmo adicionar Normalize.css ou Reset.css… ou você pode simplesmente usar um <div> e esquecê-lo. Resolver problemas rapidamente não é o que eles pagam para você? Você está com fome e isso não está ajudando em nada. Mas você é um profissional: recomponha-se e pense.

Qual é a diferença entre um <button> e um <div> ? Um <button> embutido é um elemento interativo, o que significa que pode interagir com ele. Isso é profundo. Você pode clicar nele, pode se concentrar nele usando um teclado e também transmite uma função de button acessível aos leitores de tela, possibilitando que os usuários entendam que é um botão.

Impressionante! Você não está apenas ciente do elemento <button> do HTML, mas também sabe uma coisa ou duas sobre ARIA e suporte a leitores de tela. Você pode até ter tentado VoiceOver ou NVDA para testar a acessibilidade de suas interfaces.

Então, você decidiu fazer um truque. Você não vai mexer com o estilo do navegador e fará com que o elemento pareça um botão interativo adequado para usuários que possam precisar dele. Isso é inteligente!

 <div class="button" tabindex="0" role="button"> Something </div>

Agora, não apenas parece certo, mas também pode ser focalizado através do teclado, graças ao tabindex="0" , e os leitores de tela o tratarão como um botão adequado porque você adicionou sabiamente role="button" a ele. Git commit && push então! Existem algumas tarefas adicionais para esta coisa, mas terminamos com o estilo. O que poderia dar errado? Hora do almoço. Ótimo, vamos lá!

Uma hora depois…

Foi um bom almoço! Vamos voltar à nossa coisa de clique. Precisamos concluir algumas tarefas antes de prosseguir. Vamos ver... Precisamos chamar uma função doSomething assim que o botão for clicado, e deve haver uma maneira de desabilitar o botão para que não seja clicável. Parece fácil. Vamos adicionar um ouvinte de evento a este botão:

 <script> const buttons = document.querySelectorAll('.button'); [...buttons].forEach(button => { button.addEventListener('click', doSomething); }); function doSomething() { console.log('Something!'); } </script>

Feito. O usuário agora pode clicar com o mouse na área de trabalho e tocá-lo com um dedo em uma tela sensível ao toque. Um evento de clique será acionado de forma confiável e você verá muito Algo! em seu console. Qual é a próxima tarefa?

Aguentar! Precisamos garantir que funcione da mesma forma para usuários de teclado. Como temos este tabindex="0" no botão, ele pode ser focado e, uma vez focado, os usuários devem poder pressionar a barra de espaço ou a tecla “Enter” para acionar o que estiver anexado.

Portanto, precisamos anexar outro ouvinte de evento para capturar todas as teclas e acionaremos nossa função apenas para determinadas teclas. Graças a Deus que os dispositivos de toque são inteligentes o suficiente para converter todos os toques em cliques; caso contrário, teríamos que anexar vários eventos de toque também.

 <script> const buttons = document.querySelectorAll('.button'); [...buttons].forEach(button => { button.addEventListener('click', doSomething); button.addEventListener('keyup', (event) => { if (event.key == 'Enter' || event.key == ' ') { doSomething(); } }); }); function doSomething() { console.log('Something!'); } </script>

Ufa! Agora nossa coisa clicky é totalmente acessível a partir do teclado. Estou tão orgulhoso de você! E o JavaScript é realmente mágico – o que faríamos sem ele?

Tudo bem, qual é a última tarefa: “O botão deve ter um estado desabilitado que mude sua aparência e comportamento para algo entorpecido”. Dormente? Acho que isso significa algo cinza e não responsivo à interação. OK, vamos adicionar um estado na folha de estilo usando a nomenclatura BEM.

 <div class="button button--disabled" tabindex="0" role="button"> Something </div> <style> .button--disabled { background-color: #9B9B9B; } </style> 
Botão cinza com o estado desativado aplicado
Este botão parece confortavelmente dormente. (Visualização grande)

Isso parece confortavelmente entorpecido para mim. Sempre que o botão precisar ser desabilitado, adicionaremos o modificador button--disabled para torná-lo cinza. Mas ainda não está entorpecido o suficiente: ainda pode ser focado e acionado por um ponteiro e pelo teclado.

Droga, isso está ficando complicado.

Não apenas isso, mas o botão não deve estar acessível na ordem de tabulação, o que significa que o atributo tabindex não deve estar lá. E precisamos verificar se o botão está com o estado desabilitado e então parar de acionar nossa função. Além disso, esse modificador pode ser aplicado dinamicamente. Embora não seja um problema para o CSS combinar elementos com seletores em tempo real e aplicar estilos, podemos precisar de algum tipo de observador de mutação para acionar outras alterações para este botão.

Eu sei direito? Pensamos que este seria um pequeno botão simples que aciona uma função e tem um estado desabilitado. Tentamos acertar com acessibilidade e todas essas coisas, e agora estamos no fundo dessa toca de coelho.

Vamos pegar alguma comida para viagem. Não estaremos em casa para jantar quando terminarmos e testarmos isso adequadamente. Maldito W3C! Por que eles não tentam tornar nossas vidas mais fáceis? Como se eles se importassem conosco!

Aliás, eles fazem…

Vamos dar alguns passos para trás antes de pular nessa bagunça. Por que não tentamos fazer essas coisas usando o elemento <button> ? Ele tem alguns truques úteis na manga, não apenas os estilos feios do navegador. Ah, e não se esqueça type="button" — você não quer que o botão "Fechar" do pop-up envie acidentalmente o formulário, porque type="submit" é o valor padrão.

Aparentemente, quando o <button> está focado e a barra de espaço ou a tecla “Enter” é pressionada, ele acionará o evento de click , assim como os dispositivos móveis fazem quando recebem toques, tapinhas, lambidas ou qualquer outra coisa que eles sejam capazes de receber hoje. Um ouvinte de evento a menos em nosso código! Agradável.

 // A click is enough! button.addEventListener('click', doSomething);

Quanto ao estado desabilitado, o atributo disabled está disponível para o elemento <button> , bem como para todos os elementos do formulário, incluindo <fieldset> . Sem brincadeiras. Você sabia que pode desabilitar um monte de entradas agrupadas apenas aplicando um único atributo ao pai <fieldset> ?

Um grupo de entradas desabilitadas e um botão
Um monte de entradas desabilitadas com um único atributo (visualização grande)
 <fieldset disabled> <legend>A bunch of numb inputs</legend> <p> <label> <input type="radio" name="option"> Of course it's a link </label> </p> <p> <label> <input type="radio" name="option"> Obviously, it's a button </label> </p> <p> <label> <input type="radio" name="option"> I just wanna go home </label> </p> <button type="button">Button</button> </fieldset>

Agora você sabe! Este atributo não apenas desabilita todos os eventos em elementos de formulário, mas também os remove da ordem de tabulação. Problema resolvido!

 <button disabled type="button" class="button"> Something </button>

Mas espere, tem mais! Ele também aciona a pseudo-classe :disabled em CSS, o que significa que podemos nos livrar do modificador BEM para declarar estilos e usar o modificador dinâmico embutido.

 .button:disabled { background-color: #9B9B9B; }

Quanto aos estilos feios do navegador, não precisamos usar todo o Normalize.css para corrigir um único botão. Use-o como fonte de sabedoria: As três linhas extras abaixo corrigirão a maioria das diferenças irritantes do <div> . Se você precisar de mais, você pode copiar as partes relevantes dele.

 .button { font-size: 100%; font-family: inherit; border: none; }

Feito. Afinal, HTML não é tão ruim!

Mas se isso o surpreender de vez em quando, certifique-se de verificar a especificação HTML para obter respostas. Ficou muito mais amigável ao longo dos anos e está cheio de bons exemplos de uso e acessibilidade. E, claro, o bom e velho HTML5 Doctor ainda é um lugar confiável para descobrir a diferença entre os elementos <section> e <article> e para verificar se o esboço do documento ainda é uma coisa (não realmente). Há uma boa chance de você também acabar lendo a documentação HTML da Mozilla, e você também não vai se arrepender.

Esta tarefa já está concluída! Qual é o próximo? Um calendário de carrossel suspenso com um campo de pesquisa? Oh meu! Boa sorte com isso. Mas lembre-se: o <button> é seu amigo!

Leitura adicional no SmashingMag:

  • Como projetar botões melhores
  • Como criar botões de alternância inclusivos
  • Design de botão fantasma: isso ainda é uma coisa (e por quê)?
  • Padrões de design: quando quebrar as regras está tudo bem