Catégories: Accessibilité, Code

Un div n’est pas un bouton

Les boutons, comme les liens, sont des éléments essentiels d’un site, qui permettent aux internautes d’interagir avec le contenu. Malheureusement, ils sont souvent codés avec les mauvaises balises HTML, ce qui peut causer de graves problèmes d’accessibilité. Nous allons voir comment éviter ça.

Au cours d’une conférence à laquelle j’ai assisté l’année dernière, Manuel Matuzović a évoqué un problème récurrent qu’il rencontre lors de ses audits d’accessibilité: les boutons qui n’en sont pas vraiment. Parce qu’ils sont codés avec les mauvaises balises HTML (par exemple un bon vieux <div>, une image ou un élément SVG), des boutons qui fonctionnent parfaitement avec une souris sont inutilisables pour les personnes qui naviguent au clavier ou qui utilisent un logiciel de lecture d’écran.

Bon. Depuis cette conférence, évidemment, des boutons inaccessibles j’en vois partout.


L’importance du HTML sémantique pour l’accessibilité

Une des premières choses qu’on apprend en HTML, c’est l’existence de différentes balises 1. Certaines d’entre elles sont « neutres », comme <div> ou <span>; la plupart ont un sens sémantique qui permet d’implémenter certaines fonctionnalités ou de communiquer une information sur leur contenu, par exemple aux moteurs de recherche ou aux lecteurs d’écran.

L’utilisation de balises HTML sémantiques est un des points clés de l’accessibilité du web. Les technologies d’assistance qui permettent aux personnes handicapées de naviguer sur Internet s’en servent pour analyser la structure et le contenu des pages, et le restituer de la manière la plus claire possible. On facilite ainsi la compréhension du contenu, mais aussi la navigation et l’accès à certaines fonctionnalités.

C’est quoi un bouton accessible ?

La balise HTML appropriée pour coder un bouton est <button>. En plus d’être clicable avec la souris, cet élément bénéficie de plusieurs interractions natives au clavier:

  • Il peut prendre le focus lorsqu’on navigue dans une page avec tab ou maj + tab;
  • Il est activable avec les touches entrée ou espace.

Si on choisit d’utiliser une autre balise pour créer un bouton, par exemple un <div>, on perd ces fonctionnalités qui sont indispensables pour l’accessibilité.

Toutes les personnes qui visitent votre site ne naviguent pas avec la souris; une partie des internautes se servent en effet du clavier, ou d’une technologie d’assistance basée sur la navigation au clavier. Il est donc primordial de faire en sorte que toutes les interactions soient réalisables sans l’aide d’une souris. Un bouton (ou un lien) doit également pouvoir être utilisé par les personnes qui se servent d’un logiciel de lecture d’écran. Pour cela, il doit avoir un nom accessible – par exemple un simple texte, un attribut alt ou aria-label 2.

On voit parfois aussi des « faux boutons » créés avec la balise <a> (je plaide 100% coupable, avant de me former en accessibilité web j’ignorais que c’était un problème). Même si c’est « moins pire » qu’utiliser un <div> ou une image, il s’agit également d’une mauvaise pratique: la balise <a> ne devrait pas être utilisée pour créer un bouton. Elle sert à définir un lien, qui amène en général vers une autre page. Un bouton servira plutôt à faire quelque chose, par exemple défiler un slideshow ou afficher un élément masqué. Les interactions au clavier ne sont pas les mêmes pour les balises <a> et <button>, et elles ne sont pas non plus restituées de la même manière par les lecteurs d’écran. D’une manière générale, si on utilise l’attribut href="#" dans une balise <a>, souvent accompagné de la méthode javascript preventDefault(), c’est que l’élément <button> serait sans doute plus approprié.

Sur MacOS, par défaut les liens et les boutons ne sont pas utilisables au clavier. Marie Guillaumet a rédigé un billet détaillé où elle explique comment activer la prise de focus pour tous les éléments interractifs, sur Firefox, Safari et l’ensemble du système d’exploitation.

Le cas du bouton hamburger

Le bouton hamburger qui permet d’afficher et de masquer la navigation du site est un design pattern extrêmement répandu. On le rencontre habituellement en version mobile, mais aussi de plus en plus souvent sur la version large de certains sites. S’il est mal codé, ce bouton peut causer des problèmes d’accessibilité majeurs car certains internautes ne pourront pas accéder à une partie, voire à la totalité, du contenu de votre site.

Ci-dessous, quelques exemples de code récoltés sur différents sites:

<div class="nav-toggle">
  <span></span>
  <span></span>
  <span></span>
</div>
<div class="nav-toggle">
  <svg class="burger-icon">…</svg>
</div>
<svg class="nav-toggle burger-icon">
  …
</svg>

Dans les trois cas, ces quelques lignes de HTML servent à créer visuellement le bouton hamburger.

Quand on utilise une souris, tout va bien: il suffit d’un peu de CSS pour lui donner l’apparence souhaitée, et d’une petite fonction javascript pour qu’un clic active effectivement l’affichage du menu.

Les choses se compliquent malheureusement si on s’intéresse à l’accessibilité. Notre petit bouton présente deux problèmes de HTML, qui rendent le menu de navigation totalement inaccessible pour les personnes qui naviguent au clavier ainsi que pour celles qui utilisent un lecteur d’écran:

  • Le « bouton » est créé avec une balise <div>, qui n’est pas un élément interactif. Il ne prendra donc pas le focus lorsqu’on tabule dans la page, et ne pourra pas être activé avec les touches entrée ou espace. Il n’est par conséquent pas accessible au clavier.
  • Le bouton n’a pas de nom accessible, les lecteurs d’écran ne peuvent donc pas le restituer.

On pourrait certes rendre le <div> interactif en ajoutant du javascript supplémentaire. Dans tous les exemples que j’ai vus, ce n’était toutefois jamais le cas; les développeurs n’avaient tout simplement pas pris en compte la navigation au clavier.

Et surtout: pourquoi se compliquer la vie inutilemement alors qu’un élément HTML natif existe et répond exactement à nos besoins dans un cas comme celui-ci ?

Comment rendre le bouton hamburger accessible ?

Voici un exemple très basique de bouton hamburger accessible, à la fois pour les personnes qui naviguent avec le clavier et celles qui utilisent un lecteur d’écran:

<button class="nav-toggle">
  <span class="sr-only">Menu</span>
  <span class="patty" aria-hidden="true"></span>
</button>

Par rapport aux exemples de code précédents, on effectue les trois corrections suivantes:

  1. Évidemment, on commence par remplacer la balise <div> par un <button>: le bouton est à présent un élément interactif, il peut prendre le focus et être activé avec les touches adéquates, sans qu’on ait besoin d’ajouter du code javascript supplémentaire.
  2. On ajoute un nom accessible, qu’on cache grâce à la classe .sr-only 3: il est disponible pour les lecteurs d’écran tout en étant masqué visuellement. On pourrait également décider de laisser le mot « Menu » visible pour tout le monde. En revanche, on n’utilise surtout pas display:none pour masquer le texte: il serait alors également caché pour les lecteurs d’écran.
  3. On ajoute aria-hidden="true" sur l’élément qui servira à créer l’icône hamburger, pour être sûr qu’il sera ignoré par les lecteurs d’écran. Il peut s’agir d’un élément SVG, d’une image, d’un unique <span> avec des pseudo éléments, de trois <span> distincts… du point de vue de l’accessibilité, cela n’a pas d’importance, tant qu’on masque bien cet icône pour les lecteurs d’écran.

C’est déjà pas mal, mais on peut aller encore un peu plus loin:

<button class="nav-toggle"
        aria-controls="header-menu"
        aria-expanded="false">
  <span class="sr-only open-text">Afficher le menu</span>
  <span class="sr-only close-text">Masquer le menu</span>
  <span class="patty" aria-hidden="true"></span>
</button>

<ul id="header-menu"><!-- contenu du menu --></ul>

L’ajout d’attributs ARIA nous permet d’apporter quelques informations supplémentaires qui serviront à améliorer l’accès au menu avec un lecteur d’écran:

  • aria-controls="header-menu" indique l’élément que le bouton contrôle. La valeur de l’attribut correspond à l’id de l’élément.
  • aria-expanded="false" permet d’indiquer que l’élément contrôlé par le bouton est actuellement masqué. La valeur de l’attribut doit passer à true lorsque l’élément est affiché.
  • Selon l’état du menu, on cache totalement le texte « Afficher » ou « Masquer » à l’aide de display:none. Le nom du bouton est ainsi plus explicite pour les personnes qui utilisent un lecteur d’écran.

Attention toutefois avec les attributs ARIA: bien que très utiles, ils peuvent causer de gros problèmes d’accessibilité s’ils sont mal utilisés 4. Je ne m’en sers pour ma part qu’avec parcimonie, lorsqu’aucune solution en HTML ne me permet d’obtenir le même résultat, en m’assurant qu’ils ont bien l’effet souhaité.

À ce sujet, je vous conseiller la conférence « Bien doser l’utilisation d’ARIA pour éviter les catastrophes » proposée par Bart Simons et Sophie Schuermans lors de ParisWeb en septembre 2022.

Si vous préférez utiliser un <svg> pour le pictogramme, le principe est le même et le code change très peu:

<button class="nav-toggle"
        aria-controls="header-menu"
        aria-expanded="false">
  <span class="sr-only open-text">Afficher le menu</span>
  <span class="sr-only close-text">Masquer le menu</span>
  <svg class="burger-icon" aria-hidden="true">
    <!-- contenu du svg -->
  </svg>
</button>

<ul id="header-menu"><!-- contenu du menu --></ul>

Styler les boutons

Par défaut, les boutons ont des styles qui varient d’un navigateur à l’autre. Pour tout remettre à zéro sur tous les navigateurs, rien de compliqué: quelques lignes de CSS suffisent, inspirées par les styles de normalize.css 5:

button {
  -webkit-appearance: button;
  background: transparent;
  border: 0;
  font-family: inherit;
  font-size: 100%;
  line-height: 1;
  overflow: visible;
  margin: 0;
  text-transform: none;
}

Il existe même une méthode encore plus simple que celle-ci, que j’ai découverte une fois de plus grâce à Manuel Matuzović:

button {
  all: initial;
  color: inherit;
  font: inherit;
  outline: revert;
}

La propriété all:initial désactive tous les styles de base du bouton. Avec color:inherit et font:inherit, on demande au bouton de conserver les propriétés typographiques de son parent. On s’assure également que l’outline n’est pas désacvité lorsque le bouton reçoit le focus grâce à outline: revert, qui rétablit le style de base du navigateur.

Il ne reste ensuite plus qu’à adapter l’apparence du bouton en fonction du design du site, en utilisant background-color, border, color, padding, etc. Et voilà ! Un bouton joli ET utilisable par tout le monde. Il reste toutefois un dernier point indispensable pour que les boutons soient parfaitement accessibles…

On n’oublie pas l’indicateur de focus

Trop souvent, je vois encore des sites sur lesquels la prise de focus est volontairement masquée au moyen de la propriété CSS outline:0 ou outline:none (c’est par exemple le cas du thème WordPress Divi, ce qui me met franchement en rogne). Quand on navigue au clavier au moyen de la touche de tabulation, l’indicateur de focus permet de visualiser l’élément interactif (bouton, lien, champ de formulaire) sur lequel on se trouve; si on le retire, on perd cette indication visuelle indispensable.

Le style de l’indicateur de focus est implémenté par défaut dans tous les navigateurs – il s’agit habituellement d’une bordure bleue ou noire, de 2 ou 3 px d’épaisseur. Si on n’y touche pas, tout va bien, la prise de focus sur les éléments interactifs sera visible. Si on souhaite donner à l’outline un style qui correspond davantage au design du site, il faudra faire attention à ce qu’il soit suffisamment visible, et que la couleur choisie ait bien un rapport de contraste d’au moins 3:1 par rapport aux couleurs adjacentes.

Pour styler les indicateurs de prise de focus, on utilise la pseudo-classe :focus.

button:focus {
  outline: 2px solid #005f73;
  outline-offset: 2px;
}

Par défaut, les styles définis au moyen de :focus sont visibles également au moment où on clique avec la souris. C’est la raison pour laquelle de nombreux designers sont tentés de s’en débarasser, alors qu’il s’agit d’un critère indispensable à l’accessibilité d’un site.

Pour répondre à ce problème, les navigateurs ont commencé à implémenter la pseudo-classe :focus-visible, qui permet de définir des styles uniquement lorsque le focus est donné par le clavier, et pas par la souris.

button:focus-visible {
  outline: 2px solid #005f73;
  outline-offset: 2px;
}

L’implémentation de :focus-visible est satisfaisante sur les navigateurs modernes. Comme il s’agit d’un enjeu d’accessibilité majeur, je préfère toutefois l’utiliser comme amélioration progressive pour les navigateurs qui la supportent. Sara Soueidan a rédigé un article détaillé sur les indicateurs de prise de focus, dans lequel elle propose la méthode suivante:

/* Styles de base pour :focus et :focus-visible */
button:focus,
button:focus-visible {
  outline: 2px solid #005f73;
  outline-offset: 2px;
}

/* On cache l'outline pour l'état :focus uniquement si :focus-visible est supporté */
button:focus:not(:focus-visible) {
  outline-width: 0;
}

Démonstration

Pour finir, voici une démonstration rapide (que vous pouvez également voir directement sur CodePen) pour illustrer ce billet. Dans l’exemple ci-dessous, les trois boutons fonctionnent à la souris et permettent d’afficher le menu de navigation. En revanche, seul celui de droite est également utilisable au clavier et avec un lecteur d’écran.


Ressources

  • Créer des boutons accessibles et dignes de ce nom en HTML: Julie Moynat a dédié un article très complet à la création de boutons accessibles, avec de nombreux exemples.
  • The Links vs. Buttons Showdown (Youtube): Marcy Sutton explique avec de nombreux exemples dans quels cas utiliser une balise <a> et dans quels cas une balise <button> sera plus appropriée.
  • The Button Cheat Sheet: Manuel Matuzović a compilé une liste d’exemples de boutons mal codés, dont certains interrogent vraiment sur les compétences en HTML des personnes qui les ont implémentés. Version courte: utilisez un élément <button> 😎.
  • How (Not) to Build a Button: Ben Myers propose un exemple de <div> cliquable, avant d’expliquer pourquoi il vaudrait mieux utiliser un élément <button> 🤓.

Notes et références

  1. En HTML5 il en existe un peu plus d’une centaine, et vous pouvez tester le nombre dont vous vous souvenez grâce au HTML Tags Memory Test ↩︎
  2. Lire à ce sujet Les noms accessibles dans tous leurs états, dans l’édition 2019 de 24 jours de web ↩︎
  3. Par exemple celle proposée par Scott O’Hara dans son article très complet sur les manières de cacher du contenu de manière inclusive (en anglais) ↩︎
  4. D’après le rapport 2023 du projet WebAIM Million, qui analyse chaque année les pages d’accueil d’un million de sites web, en moyenne 68% plus d’erreurs d’accessibilité sont détectées sur les pages qui utilisent ARIA (source: The WebAIM Million Project). ↩︎
  5. CSS Tricks a consacré cet article très complet à la question des styles par défaut pour les boutons. ↩︎

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Vous pouvez utiliser les balises HTML suivantes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>