Essentiel Mis à jour : 2026-06

Bouton vs lien : choisir le bon élément et ce que ça change pour le clavier

Un lien navigue, un bouton agit. Confondre les deux casse le clavier, trompe le lecteur d'écran et bloque la commande vocale. Les règles de décision, les erreurs fréquentes et les cas limites.

Table des matières

Appuyer sur Espace sur un lien ne déclenche rien. Sur un bouton, ça active l’action. Ce comportement n’est pas un bug : c’est la spécification HTML. Les navigateurs ont implémenté ce comportement depuis l’origine du web. Et tous les lecteurs d’écran, toutes les aides techniques, tous les outils de test en dépendent.

Quand un développeur utilise un <div> cliquable à la place d’un <button>, ou un <a> sans href pour déclencher une action JavaScript, ce comportement disparaît.

Ce que NVDA annonce

NVDA annonce le rôle de chaque élément interactif. Un <a href> est restitué comme “lien”. Un <button> comme “bouton”. L’utilisateur entend le rôle après l’intitulé : “Envoyer, bouton” ou “Rapport annuel 2025, lien”.

Cette distinction n’est pas cosmétique. Avec Insert + F7, NVDA extrait tous les liens de la page dans une liste navigable. Les boutons n’y apparaissent pas. Un bouton implémenté avec un <a> se retrouve dans la liste des liens, ce qui fausse la navigation par liste. Un lien implémenté avec un <button> est absent de cette liste : l’utilisateur ne peut pas y accéder par ce raccourci.

La commande vocale (Dragon NaturallySpeaking, Voice Control) utilise aussi les rôles pour cibler les éléments. Prononcer “cliquer Envoyer” active le premier élément interactif portant ce label, quel que soit son rôle. Mais si le label ne correspond pas à l’intitulé visible (parce que l’aria-label a été détourné), la commande vocale échoue.

La règle de décision

Deux questions suffisent.

L’action change-t-elle l’URL ou télécharge-t-elle un fichier ? Oui : c’est un <a href>. Un lien vers une autre page, vers une ancre, vers un fichier PDF : tous utilisent <a href>.

L’action modifie-t-elle la page sans changer l’URL ? Oui : c’est un <button>. Ouvrir une modal, soumettre un formulaire, déclencher un accordéon, lancer une recherche dans la page : tous utilisent <button>.

<!-- ✅ Lien : change l'URL -->
<a href="/contact">Nous contacter</a>

<!-- ✅ Bouton : déclenche une action sur la page -->
<button type="button" onclick="ouvrirModal()">Demander un devis</button>

<!-- ✅ Bouton : soumet un formulaire -->
<button type="submit">Envoyer le message</button>

Le CTA stylisé en bouton

Un appel à l’action (“Découvrir nos offres”, “Demander un devis”) est souvent mis en forme avec une apparence de bouton : fond coloré, coins arrondis, padding généreux. Ce style visuel ne dicte pas l’élément HTML à utiliser.

Si le CTA navigue vers une autre page, c’est un <a href>. Styler un lien comme un bouton est parfaitement valide.

<!-- ✅ CTA qui navigue : <a> avec style bouton -->
<a href="/offres" class="btn btn-primary">Découvrir nos offres</a>

<!-- ✅ CTA qui déclenche une action : <button> -->
<button type="button" class="btn btn-primary" onclick="ouvrirDevis()">
  Demander un devis
</button>

L’apparence visuelle ne change pas l’élément à choisir. La question est toujours : est-ce que ça navigue ou est-ce que ça agit ?

<a> sans href : une erreur silencieuse

Un <a> sans attribut href n’est pas un lien. C’est un texte cliquable avec une apparence de lien, mais sans rôle interactif. Le navigateur ne le place pas dans l’ordre de tabulation. Le lecteur d’écran ne l’annonce pas comme un lien.

<!-- ❌ <a> sans href : non focusable, aucun rôle -->
<a onclick="ouvrirModal()">Ouvrir la fenêtre</a>
→ NVDA : texte statique, pas annoncé comme lien
→ Tab : l'élément est ignoré
→ Espace/Entrée : rien

La correction est systématique : si l’action ne navigue pas, utiliser <button type="button">.

<!-- ✅ -->
<button type="button" onclick="ouvrirModal()">Ouvrir la fenêtre</button>
→ NVDA : "Ouvrir la fenêtre, bouton"
→ Tab : l'élément reçoit le focus
→ Entrée ou Espace : action déclenchée

type="button" est spécifié explicitement parce que la valeur par défaut d’un <button> sans attribut type est submit. Un bouton sans type placé dans un <form> soumet le formulaire au clic, même s’il est imbriqué plusieurs niveaux en dessous. Les composants réutilisables finissent souvent dans des contextes inattendus : préciser le type prévient les soumissions accidentelles difficiles à diagnostiquer.

Les trois valeurs possibles :

  • type="button" : aucun comportement par défaut, seul le gestionnaire d’événements s’exécute
  • type="submit" : soumet le formulaire parent (valeur par défaut si l’attribut est absent)
  • type="reset" : réinitialise tous les champs du formulaire parent

<div> et <span> cliquables

Un élément générique reçoit un onclick et un style de bouton. Le résultat est non focusable, sans rôle, sans support clavier.

<!-- ❌ Div cliquable : inaccessible -->
<div class="btn" onclick="valider()">Valider</div>
→ NVDA : texte statique "Valider", aucun rôle
→ Tab : sauté
→ Clavier : inopérant

Si l’élément natif est inutilisable (contrainte framework, composant hérité), la correction minimale ajoute role="button" et tabindex="0", plus un gestionnaire d’événements pour Enter et Espace :

<!-- Correction minimale si <button> est impossible -->
<div
  role="button"
  tabindex="0"
  onclick="valider()"
  onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();valider()}"
>Valider</div>

Cette approche fonctionne, mais elle est fragile : le développeur doit gérer manuellement tout ce que <button> offre nativement (focus, clavier, désactivation via disabled). La première règle de l’ARIA s’applique ici : utiliser l’élément HTML natif en premier.

Le piège de role="button" sur un <a>

Certains frameworks ou bibliothèques de composants ajoutent role="button" sur un <a href> pour unifier le style visuel. Ça crée un problème : le lecteur d’écran annonce l’élément comme un bouton, ce qui implique que Espace doit fonctionner. Mais un <a> natif ne répond pas à Espace pour la navigation : Espace fait défiler la page.

<!-- ❌ role="button" sur un <a> : Espace ne navigue pas -->
<a href="/contact" role="button">Nous contacter</a>
→ NVDA : "Nous contacter, bouton"
→ Utilisateur appuie Espace : la page défile, la navigation n'est pas déclenchée
→ Utilisateur appuie Entrée : navigation déclenchée (comportement lien conservé)

L’incohérence est confuse : l’annonce dit “bouton” mais le comportement est celui d’un lien. La correction est de retirer le role="button" et de laisser le <a href> fonctionner normalement.

Si l’objectif est de déclencher une action (pas une navigation), l’élément correct est <button>, pas <a> avec un rôle forcé.

Les boutons sans intitulé

Un bouton sans texte accessible est inopérable pour un lecteur d’écran et pour la commande vocale.

Le cas le plus courant : un bouton de fermeture de modal avec une croix en icône seule.

<!-- ❌ Bouton vide : aucun intitulé -->
<button type="button" class="modal-close">
  <svg aria-hidden="true"><use href="#icon-close"/></svg>
</button>
→ NVDA : "bouton" sans nom
→ Dragon : impossible à cibler par la voix

L’aria-label sur le <button> fournit l’intitulé :

<!-- ✅ -->
<button type="button" class="modal-close" aria-label="Fermer la fenêtre">
  <svg aria-hidden="true" focusable="false"><use href="#icon-close"/></svg>
</button>

Le même problème se pose pour les boutons d’action répétés sur une liste (“Supprimer”, “Modifier” sans contexte). L’aria-label contextualise l’action sans modifier l’intitulé visible :

<!-- ✅ Bouton contextualisé sur une liste d'articles -->
<button type="button" aria-label="Supprimer l'article Rapport annuel 2025">
  Supprimer
</button>

L’aria-label doit contenir les mots de l’intitulé visible. Écrire aria-label="Effacer" sur un bouton dont le texte visible est “Supprimer” est une non-conformité au critère WCAG 2.5.3 (Label in Name).

En audit RGAA

Trois critères sont directement liés.

Critère 7.1 : chaque composant interactif a-t-il un nom, un rôle et des états corrects ? Tester avec l’inspecteur d’accessibilité du navigateur (arbre d’accessibilité dans DevTools). Chaque lien doit avoir le rôle link, chaque bouton le rôle button. Un <div> ou <span> cliquable sans rôle est non conforme.

Critère 7.3 : chaque composant interactif est-il utilisable au clavier ? Tester en débranchant la souris. Chaque lien et chaque bouton doivent recevoir le focus via Tab. Chaque lien doit s’activer avec Entrée. Chaque bouton avec Entrée et Espace. Un <a> sans href ne reçoit pas le focus : non conforme.

Critère 11.9 : chaque bouton de formulaire a-t-il un intitulé explicite ? Vérifier chaque <button>, <input type="submit"> et <input type="button">. Un bouton avec icône seule et sans aria-label est non conforme. Un bouton avec aria-label en anglais quand le site est en français viole 2.5.3.

Checklist

  • Les liens utilisent <a href> et naviguent vers une URL ou un fichier
  • Les boutons utilisent <button type="button"> ou <button type="submit">
  • Aucun <a> sans href utilisé comme déclencheur d’action
  • Aucun <div> ou <span> cliquable sans role="button" et tabindex="0"
  • Les <div role="button"> répondent à Entrée et Espace via un listener explicite
  • Aucun role="button" posé sur un <a href> qui navigue
  • Tous les boutons avec icône seule ont un aria-label sur le <button>
  • Les aria-label de boutons contiennent les mots de l’intitulé visible (critère 2.5.3)
  • Les boutons d’action répétés (Supprimer, Modifier) sont contextualisés via aria-label

Erreurs fréquentes en audit

ErreurCritèreCorrection
<div> ou <span> cliquable sans rôle ni focus7.1, 7.3<button type="button">
<a> sans href pour déclencher une action JS7.1, 7.3<button type="button">
role="button" sur un <a href>7.3Retirer le rôle, garder le <a href>
Bouton icône sans aria-label11.9aria-label décrivant l’action sur le <button>
aria-label en anglais sur un bouton français11.9Traduire en français et vérifier que l’intitulé visible est inclus
Boutons “Supprimer” répétés sans contexte11.9aria-label="Supprimer l'article [titre]"
<div role="button"> sans gestionnaire keydown7.3Ajouter listener pour Enter et Space, ou passer à <button>

La lettre de l'Atelier A11Y

Ressources pédagogiques, critères RGAA commentés et retours de terrain : une lettre mensuelle pour progresser sur l'accessibilité numérique, sans jargon.

  • Nouveaux articles et ressources pédagogiques
  • Critères RGAA décortiqués avec des exemples concrets
  • Bonnes pratiques et retours d'expérience terrain
S'abonner à la newsletter (s'ouvre dans un nouvel onglet)

Gratuit. Désabonnement possible à tout moment.