aria-label et aria-labelledby servent à nommer un élément. Pas à lui donner un rôle. Pas à le rendre accessible par magie. Nommer un élément sans rôle ne produit rien. Et nommer un élément qui n’en a pas besoin crée du bruit.
Ce que fait concrètement un aria-label
Un lecteur d’écran annonce un élément en combinant deux informations : son rôle et son nom accessible. Le rôle vient du HTML (<nav> = “navigation”, <button> = “bouton”). Le nom accessible, c’est le contenu texte de l’élément, ou la valeur d’aria-label ou aria-labelledby si ces attributs sont présents.
Avec NVDA + Firefox, naviguer vers un <nav> sans label produit :
"navigation"
Avec aria-label="Menu principal" :
"Menu principal, navigation"
Le nom passe en premier, le rôle suit. C’est l’ordre d’annonce standard. L’utilisateur sait immédiatement de quelle navigation il s’agit, sans avoir à en parcourir le contenu.
La règle de base : label uniquement si le rôle ne suffit pas
Un seul <nav> sur la page : le rôle suffit. L’utilisateur sait qu’il y a une navigation, et il n’y en a qu’une. Ajouter aria-label="Navigation" ne fait qu’annoncer “Navigation, navigation” sur certains lecteurs d’écran. C’est redondant.
Plusieurs <nav> sur la page : là, le label devient indispensable. Sans lui, l’utilisateur entend “navigation” deux fois, sans moyen de distinguer l’une de l’autre.
Le principe s’applique à tous les éléments de type landmark :
| Élément | Label utile si… |
|---|---|
<nav> | Plusieurs <nav> sur la page |
<aside> | Plusieurs <aside> sur la page |
<header>, <footer> | Utilisés hors contexte <body> (donc sans rôle banner / contentinfo) |
<main> | Contenu qui change dynamiquement (cas rare) |
<form> | Plusieurs formulaires, ou formulaire sans titre visible |
<dialog> | Toujours (obligatoire pour que la modale soit annoncée correctement) |
<section> | Toujours (voir section suivante) |
Le cas particulier de <section>
<section> sans label accessible n’est pas exposée comme landmark. Le lecteur d’écran la traite comme un <div> générique : aucune annonce à l’entrée, aucune navigation possible via les raccourcis.
Avec aria-label ou aria-labelledby, <section> acquiert le rôle implicite region et devient navigable via la touche R sous NVDA et JAWS.
<!-- Ignorée comme landmark, traitée comme un div -->
<section>
<h2>Nos services</h2>
...
</section>
<!-- Rôle "region" actif, navigable au clavier -->
<section aria-labelledby="titre-services">
<h2 id="titre-services">Nos services</h2>
...
</section>
La deuxième forme est préférable à aria-label quand un titre visible existe déjà : aria-labelledby pointe vers ce titre et évite de dupliquer le texte.
aria-label vs aria-labelledby : lequel choisir
aria-label contient directement le texte du nom accessible. aria-labelledby pointe vers un élément existant dans le DOM via son id.
<!-- aria-label : texte directement dans l'attribut -->
<nav aria-label="Navigation secondaire">...</nav>
<!-- aria-labelledby : texte récupéré depuis un élément existant -->
<section aria-labelledby="titre-contact">
<h2 id="titre-contact">Nous contacter</h2>
...
</section>
aria-labelledby est à privilégier quand un titre visible peut servir de référence : le nom accessible et le contenu visuel restent synchronisés, et le titre n’est pas répété dans le code. aria-label s’utilise quand aucun texte visible ne convient, par exemple pour un bouton icône ou une navigation dont le titre n’est pas affiché.
Les cas où le label est ignoré
C’est ici que les erreurs silencieuses se produisent. Aucune alerte, aucun message d’erreur dans les outils d’audit, mais le label n’est pas lu.
Un élément sans rôle sémantique significatif
aria-label sur un <div> ou un <span> sans attribut role : ignoré. L’élément n’a pas de point d’annonce.
<!-- Ignoré : div sans rôle -->
<div aria-label="Section actualités">...</div>
<!-- Fonctionnel : rôle explicite -->
<div role="region" aria-label="Actualités">...</div>
Une cible masquée avec display: none ou visibility: hidden
aria-labelledby récupère le contenu d’un élément référencé. Si cet élément est masqué via display: none ou visibility: hidden, son contenu texte n’est pas accessible à l’arbre d’accessibilité : le label est vide.
Le masquage .sr-only (clip technique) fonctionne, lui : l’élément est retiré visuellement mais reste dans l’arbre d’accessibilité.
/* Masquage qui coupe l'accès : aria-labelledby ne peut pas récupérer ce texte */
.masque { display: none; }
/* Masquage qui préserve l'accès */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
}
Un id absent, mal orthographié ou dupliqué
aria-labelledby ne produit aucune erreur si l’id référencé n’existe pas. Le label est simplement ignoré. Un id dupliqué dans la page (erreur fréquente dans les composants réutilisables) produit le même résultat : le comportement est indéfini.
<!-- id="titre-nav" inexistant dans le DOM : label silencieux -->
<nav aria-labelledby="titre-nav">...</nav>
<!-- Fonctionne -->
<nav aria-labelledby="titre-navigation-principale">
<h2 id="titre-navigation-principale" class="sr-only">Navigation principale</h2>
...
</nav>
aria-hidden="true" sur la cible
Si l’élément référencé par aria-labelledby porte aria-hidden="true", son contenu est masqué à l’arbre d’accessibilité : le label est ignoré.
Tableau récapitulatif
| Situation | aria-label lu ? | Raison |
|---|---|---|
<nav aria-label="..."> (un seul nav) | Oui, mais inutile | Redondant avec le rôle |
<nav aria-label="..."> (plusieurs nav) | Oui | Disambiguïsation nécessaire |
<section aria-label="..."> | Oui | Crée le rôle region |
<section> sans label | Non annoncée | Pas de rôle landmark |
<div aria-label="..."> sans role | Non | Pas de point d’annonce |
aria-labelledby → display: none | Non | Élément absent de l’arbre |
aria-labelledby → .sr-only | Oui | Accessible mais masqué visuellement |
aria-labelledby → id introuvable | Non | Référence invalide, silencieuse |
aria-labelledby → aria-hidden="true" | Non | Exclu de l’arbre d’accessibilité |
En pratique pour un audit RGAA
Le critère 12.6 demande que chaque groupe de liens (navigation) soit identifié. Les vérifications à faire :
- Compter les
<nav>sur la page. Si plusieurs : vérifier que chacun a un label distinct. - Sur les
<section>, vérifier la présence d’unaria-labelouaria-labelledby. Sans ça, la section n’est pas un landmark. - Pour chaque
aria-labelledby, vérifier que l’idréférencé existe, est unique, et que l’élément ciblé n’est nidisplay: noneniaria-hidden. - Tester avec NVDA (Firefox) et VoiceOver (Safari) : l’annonce “Nom, rôle” doit être explicite et non redondante.
Les outils automatiques (axe, WAVE) détectent les <nav> sans label quand il y en a plusieurs. Ils ne détectent pas les labels silencieux dus à une cible mal masquée ou un id dupliqué. Ces vérifications sont manuelles.