Aria-expanded : implémenter correctement les états dépliés
En Bref : L'essentiel à retenir
- aria-expanded indique si un élément contrôlant du contenu masquable est actuellement déplié (true) ou replié (false).
- L'attribut doit être sur l'élément déclencheur (bouton) et mis à jour dynamiquement avec JavaScript lors de chaque interaction.
- Combinez aria-expanded avec aria-controls pour lier le bouton au panneau contrôlé et hidden pour masquer le contenu replié.
- 58% des composants dépliables échouent à implémenter aria-expanded selon WebAIM, causant une confusion pour les utilisateurs de lecteurs d'écran.
Les interfaces modernes regorgent d'éléments qui se déploient : accordéons, menus déroulants, panneaux de détails. Sans aria-expanded, les utilisateurs de lecteurs d'écran ne savent pas si un élément est ouvert ou fermé. Voici comment l'implémenter correctement.
Comprendre aria-expanded
aria-expanded est un attribut d'état ARIA qui communique si un élément dépliant est actuellement :
true: le contenu associé est visible/dépliéfalse: le contenu associé est masqué/replié- Non défini : l'élément n'a pas de comportement dépliant
[!NOTE] Selon une analyse WebAIM de 2023, 58% des composants dépliables sur le web n'implémentent pas correctement aria-expanded. C'est l'une des erreurs ARIA les plus fréquentes.
Où placer l'attribut
L'attribut se place sur l'élément déclencheur (généralement un bouton), pas sur le contenu qui se déploie :
<!-- Correct : sur le bouton -->
<button aria-expanded="false" aria-controls="panel-1">
Voir les détails
</button>
<div id="panel-1" hidden>
Contenu du panneau...
</div>
<!-- Incorrect : sur le panneau -->
<button>Voir les détails</button>
<div aria-expanded="false">
Contenu du panneau...
</div>
Implémentation basique avec JavaScript
Voici un pattern complet pour un bouton dépliable :
<button
id="toggle-btn"
aria-expanded="false"
aria-controls="content-panel">
Afficher plus d'informations
</button>
<div id="content-panel" hidden>
<p>Contenu supplémentaire ici...</p>
</div>
const button = document.getElementById('toggle-btn');
const panel = document.getElementById('content-panel');
button.addEventListener('click', () => {
const isExpanded = button.getAttribute('aria-expanded') === 'true';
// Basculer l'état
button.setAttribute('aria-expanded', !isExpanded);
// Masquer/afficher le panneau
panel.hidden = isExpanded;
});
Accordéon complet
Un accordéon avec plusieurs sections où une seule peut être ouverte :
<div class="accordion">
<h3>
<button aria-expanded="false" aria-controls="section-1">
Section 1
</button>
</h3>
<div id="section-1" class="panel" hidden>
Contenu de la section 1...
</div>
<h3>
<button aria-expanded="false" aria-controls="section-2">
Section 2
</button>
</h3>
<div id="section-2" class="panel" hidden>
Contenu de la section 2...
</div>
</div>
document.querySelectorAll('.accordion button').forEach(button => {
button.addEventListener('click', () => {
const isExpanded = button.getAttribute('aria-expanded') === 'true';
// Fermer toutes les sections
document.querySelectorAll('.accordion button').forEach(btn => {
btn.setAttribute('aria-expanded', 'false');
});
document.querySelectorAll('.accordion .panel').forEach(panel => {
panel.hidden = true;
});
// Ouvrir la section cliquée (si elle était fermée)
if (!isExpanded) {
button.setAttribute('aria-expanded', 'true');
document.getElementById(
button.getAttribute('aria-controls')
).hidden = false;
}
});
});
Consultez notre guide complet sur les accordéons accessibles pour les patterns clavier.
Menu déroulant navigation
Pour un menu de navigation avec sous-menus :
<nav>
<ul>
<li>
<button aria-expanded="false" aria-controls="submenu-1">
Produits
<svg aria-hidden="true"><!-- icône flèche --></svg>
</button>
<ul id="submenu-1" hidden>
<li><a href="/produit-a">Produit A</a></li>
<li><a href="/produit-b">Produit B</a></li>
</ul>
</li>
</ul>
</nav>
[!TIP] Pour les menus, ajoutez la fermeture avec Échap et le clic extérieur pour une meilleure ergonomie.
Attributs complémentaires
aria-controls
Lie le bouton au panneau qu'il contrôle :
<button aria-expanded="false" aria-controls="details-panel">
Plus de détails
</button>
<div id="details-panel" hidden>...</div>
Le support de aria-controls est limité (principalement JAWS), mais c'est une bonne pratique de documentation du code.
hidden vs display: none
L'attribut HTML hidden est préférable à display: none en CSS car il est sémantique et compris par tous les navigateurs et technologies d'assistance.
<!-- Préféré -->
<div id="panel" hidden>...</div>
<!-- Alternative CSS si besoin d'animation -->
<div id="panel" class="collapsed">...</div>
Synchroniser l'icône visuelle
Si vous utilisez une icône pour indiquer l'état (flèche, +/-), masquez-la aux lecteurs d'écran :
<button aria-expanded="false">
Section FAQ
<svg aria-hidden="true" class="icon-chevron">
<!-- L'icône pivote via CSS selon l'état -->
</svg>
</button>
button[aria-expanded="true"] .icon-chevron {
transform: rotate(180deg);
}
L'information est déjà transmise par aria-expanded, l'icône est purement visuelle.
Erreurs courantes
1. Oublier de mettre à jour l'attribut
// Mauvais : l'état ARIA n'est jamais mis à jour
button.addEventListener('click', () => {
panel.classList.toggle('visible');
});
// Bon : l'état ARIA suit le visuel
button.addEventListener('click', () => {
const isExpanded = button.getAttribute('aria-expanded') === 'true';
button.setAttribute('aria-expanded', !isExpanded);
panel.hidden = isExpanded;
});
2. Utiliser aria-expanded sans contenu contrôlé
<!-- Incorrect : pas de contenu à déplier -->
<button aria-expanded="false">
Envoyer le formulaire
</button>
<!-- aria-expanded n'est pas adapté ici -->
N'utilisez aria-expanded que pour les éléments qui affichent/masquent réellement du contenu.
3. État initial incohérent
<!-- Incohérent : aria-expanded="true" mais contenu masqué -->
<button aria-expanded="true">Section</button>
<div hidden>Contenu...</div>
L'état ARIA doit toujours refléter l'état visuel réel.
Quand ne PAS utiliser aria-expanded
- Liens simples : un lien qui navigue vers une nouvelle page
- Boutons de soumission : formulaires classiques
- Onglets : utilisez plutôt
aria-selectedsur les tabs - Cases à cocher : l'état coché/non coché est déjà communiqué
Navigation clavier attendue
Pour un comportement conforme au pattern WAI-ARIA :
| Touche | Action |
|---|---|
| Entrée/Espace | Bascule l'état déplié |
| Flèche bas | (Optionnel) Ouvre et déplace le focus dans le panneau |
| Échap | Ferme le panneau et remet le focus sur le bouton |
button.addEventListener('keydown', (e) => {
if (e.key === 'Escape' &&
button.getAttribute('aria-expanded') === 'true') {
button.setAttribute('aria-expanded', 'false');
panel.hidden = true;
button.focus();
}
});
Test avec lecteur d'écran
Avec NVDA ou VoiceOver, le bouton doit être annoncé comme :
- "Afficher plus d'informations, bouton réduit" (quand fermé)
- "Afficher plus d'informations, bouton déplié" (quand ouvert)
Si vous n'entendez pas l'état, vérifiez votre implémentation.
Critères RGAA concernés
| Critère | Exigence |
|---|---|
| 7.1 | Scripts compatibles avec les technologies d'assistance |
| 7.3 | Composants accessibles au clavier |
| 10.8 | Changements de contexte non déclenchés automatiquement |
Conclusion
aria-expanded est essentiel pour rendre les interfaces dépliables accessibles. La clé : synchroniser l'état ARIA avec l'état visuel, et toujours placer l'attribut sur l'élément déclencheur. Testez avec un lecteur d'écran pour valider le comportement.
Vérifiez l'implémentation de vos composants interactifs avec RGAA Checker : notre outil analyse les attributs ARIA et détecte les incohérences.
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.
L'intitulé de chaque lien doit être explicite et permettre de comprendre la destination ou la fonction du lien, même hors contexte.
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.