DEV Community

Vinícius Hoyer
Vinícius Hoyer

Posted on • Edited on

4 4

Na dúvida, use um botão

Tá, eu entendo você dizer que acessibilidade é difícil, é mesmo, mas eu acho que, mesmo sendo difícil, a gente tem que se atentar a isso, pois estamos fazendo com que informações não possam ser consumidas por certas pessoas; e porque você não quer que essas pessoas costumam seu conteúdo? Tu é preconceituoso, é? Eu espero que não 😅.

Então devemos sempre tentar dar o nosso máximo para deixar nossos sites o mais acessível que conseguirmos. Motivado por isso venho aqui aplicar todos os meus conhecimentos para tentar te ensinar a fazer um botão acessível!

Eu tenho esse botão aqui, como eu deixo ele acessível?

<style>
.my-button {
  outline: none;
  background: hsl(80deg, 50%, 50%);
  color: white;
  display: inline-block;
  padding: 4px 16px;
}
.my-button:hover {
  background: hsl(80deg, 50%, 30%);
}
.my-button:active {
  background: hsl(80deg, 50%, 60%);
}
</style>
<div class="my-button">
  Click me
</div>
<script>
document.querySelector(".my-button").addEventListener('click', () => alert("poke!"))
</script>
Enter fullscreen mode Exit fullscreen mode

Antes de qualquer coisa, eu queria só dar o disclaimer de que o botão não é feio, você que não entende a sutil arte por traz do design dele, a arte de: escrever-o-código-chutando-números-e-só-ver-o-resultado-depois-de-terminar-de-escrever-seu-post, pode usar essa técnica, a licença dela é MIT.

Antes de tudo!

Ih, rapaz, olha, é considerada péssima prática colocar outline: none. Se você não pode evitar, converse com o responsável por não deixar você tirar o none do outline no elemento, para essa pessoa achar um substituto bom o suficiente, com um contraste legal pra você colocar no lugar 😉.

@@ -1,6 +1,5 @@
 <style>
 .my-button {
-  outline: none;
   background: hsl(80deg, 50%, 50%);
   color: white;
   display: inline-block;
Enter fullscreen mode Exit fullscreen mode

Primeira coisa

O primeiro problema que eu noto é que: se o usuário estiver navegando no seu site exclusivamente com o teclado — seja por paralisia, braço quebrado ou porque ele tá segurando um sanduíche com a outra mão — esse usuário não vai conseguir apertar esse botão, porque ele não é focável. Tenta você selecionar esse botão só com Tab.

Para resolver esse problema, você pode definir explicitamente no HTML que o .my-button é focável com: tabindex="0". O tal do tabindex, com valor 0, serve pra dizer pro browser que esse elemento pode sim ser focado, independentemente de qual tag que você está usando.

@@ -11,6 +11,6 @@
   background: hsl(80deg, 50%, 60%);
 }
 </style>
-<div class="my-button">
+<div class="my-button" tabindex="0">
   Click me
 </div>
Enter fullscreen mode Exit fullscreen mode

Segunda coisa

Quando o leitor de tela estiver lendo esse elemento (agora que ele é focável), ele vai ler algo como "Click me, generic container", as vezes só "Click me". O porquê disso é que o elemento por traz desse botão é uma div, a técnica que da para a gente usar para contornar esse problema pode ser a seguinte:

@@ -11,6 +11,6 @@
   background: hsl(80deg, 50%, 60%);
 }
 </style>
-<div class="my-button" tabindex="0">
+<div class="my-button" tabindex="0" role="button">
   Click me
 </div>
Enter fullscreen mode Exit fullscreen mode

Agora com o atributo role o leitor de tela consegue saber que essa div, na verdade, está cumprindo papel de botão e então vai ler: "Click me, button".

Terceira coisa

Quando você coloca um onClick handler na div, esse vai ser disparado quando um usuário clicar nele, mas se o usuário não estiver usando um mouse, ele vai esperar que a ação desse botão — que foi focado usando Tab — seja disparado usando uma das seguintes teclas:

  • Enter
  • Espaço

São expectativas que tem que ser cumpridas, caso contrario, o sistema pode se tornar impossível de ser utilizado. Então para corrigir isso a gente vai ter que disparar a ação desse botão no keydown dele:

@@ -17,5 +17,13 @@
   Click me
 </div>
 <script>
-document.querySelector(".my-button").addEventListener(() => alert("poke!"))
+const myButtonAction = () => alert("poke!")
+const myButton = document.querySelector(".my-button")
+
+myButton.addEventListener('click', myButtonAction)
+myButton.addEventListener('keydown', (event) => {
+  if (["Enter", " ", "Spacebar" /*ie11*/].includes(event.key)) {
+    event.preventDefault() // para evitar que a página seja scrollada (spacebar)
+    myButtonAction()
+  }
+})
 </script>
Enter fullscreen mode Exit fullscreen mode

Tem uma página da WAI-ARIA, que lista todos os comportamentos que um dado elemento, seja ele um button, checkbox, slider, list, toolbars, e etc; devem cumprir para ir de encontro com a expectativa de um dado usuário, a WCAG 1.1 authoring practices, é um documento de 1999 que foi revisado diversas vezes, e acredito que continua, a WAI-ARIA já lançou um WCAG 2.0 e um WCAG 2.1, mas eles não necessariamente são melhores que o 1.1, pessoalmente eu acho que o 1.1 é melhor organizado.

Pra quem olha esses documentos com cara de especificação e tem um troço, existe esse outro site que é o WebAIM: WCAG 2 Checklist, que transforma o WCAG em algo mais amigável, apresentando seus critérios em forma de checklist.

Mesmo assim, na minha humilde opinião, o lugar mais fácil, estruturado, e completo, para se achar essas informações é o MDN, que acabei de descobrir que da exatamente o mesmo exemplo que eu to dando agora! Cara, MDN é incrível.

No final a gente fica com isso:

<style>
.my-button {
  background: hsl(80deg, 50%, 50%);
  color: white;
  display: inline;
  padding: 4px 16px;
}
.my-button:hover {
  background: hsl(80deg, 50%, 30%);
}
.my-button:active {
  background: hsl(80deg, 50%, 60%);
}
</style>
<div class="my-button" tabindex="0" role="button">
  Click me
</div>
<script>
const myButtonAction = () => alert("poke!")
const myButton = document.querySelector(".my-button")

myButton.addEventListener('click', myButtonAction)
myButton.addEventListener('keydown', (event) => {
  if (["Enter", " ", "Spacebar" /*ie11*/].includes(event.key)) {
    event.preventDefault() // para evitar que a página seja scrollada (spacebar)
    myButtonAction()
  }
})
</script>
Enter fullscreen mode Exit fullscreen mode

Tá, mas isso parece muito complicado!

E é mesmo! Mas sabe outro jeito de fazer um botão acessível com tudo o que foi citado acima? Assim:

<style>
.my-button {
  background: hsl(80deg, 50%, 50%);
  border: none;
  color: white;
  padding: 4px 16px;
}
.my-button:hover {
  background: hsl(80deg, 50%, 30%);
}
.my-button:active {
  background: hsl(80deg, 50%, 60%);
}
</style>
<button class="my-button">
  Click me
</button>
<script>
document.querySelector(".my-button").addEventListener('click', () => alert("poke!"))
</script>
Enter fullscreen mode Exit fullscreen mode

Tem tudo o que o botão de div lá em cima tem, só que você não precisa fazer nada de mais, hehe. E o diff final fica assim:

@@ -1,21 +1,20 @@
 <style>
 .my-button {
-  outline: none;
   background: hsl(80deg, 50%, 50%);
+  border: none;
   color: white;
-  display: inline-block;
   padding: 4px 16px;
 }
 .my-button:hover {
   background: hsl(80deg, 50%, 30%);
 }
 .my-button:active {
   background: hsl(80deg, 50%, 60%);
 }
 </style>
-<div class="my-button">
+<button class="my-button">
   Click me
-</div>
+</button>
 <script>
 document.querySelector(".my-button").addEventListener('click', () => alert("poke!"))
 </script>

Enter fullscreen mode Exit fullscreen mode

Mas e se eu usar um <a>?

Se a tag não tiver um atributo href ele é considerado, praticamente, uma div, então todo o trabalho que deu pra transformar uma <div> num botão, vai ser o mesmo para fazer com uma tag <a>.

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (3)

Collapse
 
isabelcmdcosta profile image
Isabel Costa

O primeiro artigo em Português que vejo aqui no Dev 👏🏾

Adorei o artigo! Já aprendi alguma coisa sobre acessibilidade hoje :)

Great great article! 🎉

Collapse
 
vhoyer profile image
Vinícius Hoyer

opa, :D, fico muitississississimo feliz em saber que você gostou SZ.

Confesso que antes de postar eu dei uma conferida pra ver se tinha mais gente postando em português 😅.

Daqui a uns dias sai mais um sobre testes unitários em componentes :D

Collapse
 
isabelcmdcosta profile image
Isabel Costa

Fico feliz deste post tão interessante estar a representar a lingua portuguesa :)

Eu ainda não fiz nenhum post em Português, mas já tive perto de traduzir um post meu sobre ultrapassar bloqueios de contribuir para Open Source. E quero fazê-lo. Acho muito interessante ter artigos em português também e não apenas ficar pelo inglês, que pode ser mais fácil (para mim) mas não ajuda quem fala portuês nativamente e não se sente confortável com inglês.

Artigo sobre testes unitários em componentes é ótimo, tenho andado a querer aprender mais sobre isso :D

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs