DEV Community

Vinícius Hoyer
Vinícius Hoyer

Posted on • Updated on

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>.

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