Créer des onglets accessibles : rôles ARIA et navigation clavier
En Bref : L'essentiel à retenir
- Les onglets utilisent les rôles tablist, tab et tabpanel pour communiquer leur structure aux technologies d'assistance.
- La navigation entre onglets se fait avec les flèches gauche/droite, Tab permettant de passer au contenu du panneau.
- L'onglet actif a aria-selected="true" et tabindex="0", les autres ont aria-selected="false" et tabindex="-1".
- L'activation automatique (changement de panneau au focus) est recommandée pour une meilleure expérience utilisateur.
Les interfaces à onglets sont un pattern de navigation courant pour organiser du contenu en sections. Cependant, leur implémentation accessible requiert une attention particulière aux rôles ARIA et à la navigation clavier. Ce guide vous montre comment créer des onglets conformes aux standards d'accessibilité.
Structure sémantique des onglets
Un système d'onglets accessible se compose de trois éléments principaux :
- Tablist : Le conteneur de la liste d'onglets
- Tabs : Les onglets eux-mêmes (boutons de sélection)
- Tabpanels : Les panneaux de contenu associés
HTML sémantique avec ARIA
<div class="tabs">
<!-- Liste des onglets -->
<div role="tablist" aria-label="Options de livraison">
<button
role="tab"
id="tab-standard"
aria-selected="true"
aria-controls="panel-standard"
tabindex="0"
>
Livraison standard
</button>
<button
role="tab"
id="tab-express"
aria-selected="false"
aria-controls="panel-express"
tabindex="-1"
>
Livraison express
</button>
<button
role="tab"
id="tab-relais"
aria-selected="false"
aria-controls="panel-relais"
tabindex="-1"
>
Point relais
</button>
</div>
<!-- Panneaux de contenu -->
<div
role="tabpanel"
id="panel-standard"
aria-labelledby="tab-standard"
tabindex="0"
>
<h3>Livraison standard</h3>
<p>Livraison en 3-5 jours ouvrables...</p>
</div>
<div
role="tabpanel"
id="panel-express"
aria-labelledby="tab-express"
tabindex="0"
hidden
>
<h3>Livraison express</h3>
<p>Livraison en 24-48h...</p>
</div>
<div
role="tabpanel"
id="panel-relais"
aria-labelledby="tab-relais"
tabindex="0"
hidden
>
<h3>Point relais</h3>
<p>Retirez votre colis dans un point relais...</p>
</div>
</div>
Les attributs ARIA en détail
role="tablist"
Conteneur qui regroupe les onglets. Doit avoir un label accessible :
<!-- Via aria-label -->
<div role="tablist" aria-label="Sections du produit">
<!-- Ou via aria-labelledby -->
<h2 id="titre-tabs">Spécifications</h2>
<div role="tablist" aria-labelledby="titre-tabs">
role="tab"
Chaque onglet individuel. Attributs requis :
| Attribut | Description |
|---|---|
aria-selected | true pour l'onglet actif, false pour les autres |
aria-controls | ID du tabpanel associé |
tabindex | 0 pour l'onglet actif, -1 pour les autres |
<button
role="tab"
aria-selected="true"
aria-controls="panel-1"
tabindex="0"
>
Onglet actif
</button>
role="tabpanel"
Le panneau de contenu associé à un onglet :
| Attribut | Description |
|---|---|
aria-labelledby | ID de l'onglet associé |
tabindex="0" | Rend le panneau focusable (utile si pas d'élément interactif) |
hidden | Masque les panneaux non actifs |
<div
role="tabpanel"
aria-labelledby="tab-1"
tabindex="0"
>
Contenu du panneau...
</div>
[!NOTE] Le
tabindex="0"sur le tabpanel permet aux utilisateurs clavier d'accéder au contenu même s'il ne contient pas d'éléments focusables. C'est particulièrement utile pour les lecteurs d'écran.
Navigation clavier
Le pattern ARIA Authoring Practices définit une navigation spécifique pour les onglets.
Comportement attendu
| Touche | Action |
|---|---|
| Tab | Entre dans le tablist (sur l'onglet actif), puis passe au tabpanel |
| Flèche gauche | Onglet précédent |
| Flèche droite | Onglet suivant |
| Home | Premier onglet |
| End | Dernier onglet |
| Entrée/Espace | Active l'onglet (si activation manuelle) |
Roving tabindex
La technique du "roving tabindex" permet une navigation fluide :
- L'onglet actif a
tabindex="0"et peut recevoir le focus via Tab - Les autres onglets ont
tabindex="-1"et sont accessibles via les flèches
function activerOnglet(tablist, ongletCible) {
const onglets = tablist.querySelectorAll('[role="tab"]');
onglets.forEach(onglet => {
const estActif = onglet === ongletCible;
// Mise à jour de l'état
onglet.setAttribute('aria-selected', estActif);
onglet.setAttribute('tabindex', estActif ? '0' : '-1');
// Affichage du panneau correspondant
const panneau = document.getElementById(
onglet.getAttribute('aria-controls')
);
panneau.hidden = !estActif;
});
// Focus sur l'onglet activé
ongletCible.focus();
}
Activation automatique vs manuelle
Activation automatique (recommandée)
L'onglet s'active dès qu'il reçoit le focus (via flèches). C'est le comportement recommandé par le W3C pour la plupart des cas :
onglet.addEventListener('focus', () => {
activerOnglet(tablist, onglet);
});
Avantages :
- Expérience plus fluide
- Moins de manipulations requises
- Comportement prévisible
Activation manuelle
L'utilisateur doit appuyer sur Entrée ou Espace pour activer l'onglet après l'avoir sélectionné :
onglet.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
activerOnglet(tablist, onglet);
}
});
Quand l'utiliser :
- Contenu lourd à charger (évite les chargements intempestifs)
- Onglets nombreux à parcourir
[!TIP] Dans le doute, privilégiez l'activation automatique. Elle correspond aux attentes de la majorité des utilisateurs et réduit la charge cognitive.
Implémentation JavaScript complète
class AccessibleTabs {
constructor(container) {
this.container = container;
this.tablist = container.querySelector('[role="tablist"]');
this.tabs = Array.from(container.querySelectorAll('[role="tab"]'));
this.panels = Array.from(container.querySelectorAll('[role="tabpanel"]'));
this.activeIndex = 0;
this.init();
}
init() {
this.tabs.forEach((tab, index) => {
// Click pour activer
tab.addEventListener('click', () => this.activerOnglet(index));
// Navigation clavier
tab.addEventListener('keydown', (e) => this.gererClavier(e, index));
});
}
activerOnglet(index) {
// Désactiver l'ancien onglet
this.tabs[this.activeIndex].setAttribute('aria-selected', 'false');
this.tabs[this.activeIndex].setAttribute('tabindex', '-1');
this.panels[this.activeIndex].hidden = true;
// Activer le nouveau
this.activeIndex = index;
this.tabs[index].setAttribute('aria-selected', 'true');
this.tabs[index].setAttribute('tabindex', '0');
this.tabs[index].focus();
this.panels[index].hidden = false;
}
gererClavier(event, currentIndex) {
let nouvelIndex = currentIndex;
switch (event.key) {
case 'ArrowLeft':
nouvelIndex = currentIndex > 0
? currentIndex - 1
: this.tabs.length - 1;
break;
case 'ArrowRight':
nouvelIndex = currentIndex < this.tabs.length - 1
? currentIndex + 1
: 0;
break;
case 'Home':
nouvelIndex = 0;
break;
case 'End':
nouvelIndex = this.tabs.length - 1;
break;
default:
return; // Ne rien faire pour les autres touches
}
event.preventDefault();
this.activerOnglet(nouvelIndex);
}
}
// Initialisation
document.querySelectorAll('.tabs').forEach(container => {
new AccessibleTabs(container);
});
Onglets verticaux
Pour des onglets disposés verticalement, adaptez la navigation :
<div role="tablist" aria-orientation="vertical" aria-label="Menu">
Changez les flèches utilisées :
gererClavier(event, currentIndex) {
const isVertical = this.tablist.getAttribute('aria-orientation') === 'vertical';
switch (event.key) {
case 'ArrowUp':
if (isVertical) nouvelIndex = currentIndex - 1;
break;
case 'ArrowDown':
if (isVertical) nouvelIndex = currentIndex + 1;
break;
case 'ArrowLeft':
if (!isVertical) nouvelIndex = currentIndex - 1;
break;
case 'ArrowRight':
if (!isVertical) nouvelIndex = currentIndex + 1;
break;
}
}
Styles CSS accessibles
Focus visible
Assurez un indicateur de focus clair sur les onglets :
[role="tab"]:focus {
outline: none; /* On remplace par un style custom */
}
[role="tab"]:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 2px;
}
Indication visuelle de l'onglet actif
L'état actif doit être clairement visible, pas seulement par la couleur :
[role="tab"][aria-selected="true"] {
background: #ffffff;
border-bottom: 3px solid #005fcc;
font-weight: bold;
}
[role="tab"][aria-selected="false"] {
background: #f0f0f0;
border-bottom: 1px solid #cccccc;
}
Contraste suffisant
Respectez les ratios de contraste WCAG :
- 4.5:1 pour le texte des onglets
- 3:1 pour les bordures et indicateurs visuels
Utilisez notre vérificateur de contraste.
Erreurs courantes à éviter
Utiliser des liens au lieu de boutons
Les onglets ne naviguent pas vers une nouvelle page, ce sont des boutons :
<!-- Incorrect -->
<a href="#panel-1" role="tab">Onglet 1</a>
<!-- Correct -->
<button role="tab">Onglet 1</button>
Oublier aria-controls
Chaque onglet doit être lié à son panneau :
<button role="tab" aria-controls="panel-1">Onglet</button>
<div role="tabpanel" id="panel-1">...</div>
tabindex incorrect
Seul l'onglet actif doit avoir tabindex="0" :
<!-- Incorrect : tous avec tabindex="0" -->
<button role="tab" tabindex="0">Onglet 1</button>
<button role="tab" tabindex="0">Onglet 2</button>
<!-- Correct : roving tabindex -->
<button role="tab" aria-selected="true" tabindex="0">Onglet 1</button>
<button role="tab" aria-selected="false" tabindex="-1">Onglet 2</button>
Contenu de panneau non accessible
Si le panneau ne contient pas d'éléments focusables, ajoutez tabindex="0" :
<div role="tabpanel" tabindex="0">
<p>Texte sans éléments interactifs</p>
</div>
Test d'accessibilité
Validez votre implémentation :
- Clavier : Naviguer uniquement au clavier (Tab, flèches)
- Lecteur d'écran : Vérifier les annonces (NVDA, VoiceOver)
- Focus visible : Indicateur de focus sur chaque onglet
- État annoncé : "Onglet 1, sélectionné, onglet, 1 sur 3"
- Panneaux : Le contenu est-il annoncé correctement ?
Lancez un audit RGAA complet pour vérifier l'accessibilité de votre site.
Conclusion
Les interfaces à onglets accessibles reposent sur une utilisation correcte des rôles ARIA (tablist, tab, tabpanel) et une navigation clavier conforme aux attentes des utilisateurs. Le pattern du roving tabindex garantit une expérience fluide pour les utilisateurs clavier.
En implémentant ces techniques, vous offrez une navigation efficace à tous vos utilisateurs, qu'ils utilisent une souris, un clavier, ou un lecteur d'écran.
Guides RGAA associés
Pour aller plus loin sur les sujets abordés dans cet article, consultez nos fiches techniques :
Chaque champ de formulaire doit avoir une étiquette (label) qui lui est liée explicitement.
Chaque image porteuse d'information doit avoir une alternative textuelle pertinente via l'attribut alt. Les images décoratives doivent avoir un attribut alt vide.
Dans chaque page web, un lien d'évitement ou d'accès rapide au contenu principal doit être présent, visible au focus, et placé en premier élément focusable.
Articles similaires
Votre site est-il conforme ?
Ne prenez pas de risques avec l'accessibilité. Lancez un audit complet de votre site en quelques minutes et obtenez un rapport détaillé des corrections à apporter.