Aller au contenu principal
Technique3 mars 20267 min

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.
ARIAJavaScriptAccordéonRGAAAccessibilité

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 :

CODE
<!-- 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 :

CODE
<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>
CODE
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 :

CODE
<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>
CODE
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.

Pour un menu de navigation avec sous-menus :

CODE
<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 :

CODE
<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.

CODE
<!-- 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 :

CODE
<button aria-expanded="false">
  Section FAQ
  <svg aria-hidden="true" class="icon-chevron">
    <!-- L'icône pivote via CSS selon l'état -->
  </svg>
</button>
CODE
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

CODE
// 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é

CODE
<!-- 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

CODE
<!-- 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-selected sur les tabs
  • Cases à cocher : l'état coché/non coché est déjà communiqué

Pour un comportement conforme au pattern WAI-ARIA :

ToucheAction
Entrée/EspaceBascule l'état déplié
Flèche bas(Optionnel) Ouvre et déplace le focus dans le panneau
ÉchapFerme le panneau et remet le focus sur le bouton
CODE
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èreExigence
7.1Scripts compatibles avec les technologies d'assistance
7.3Composants accessibles au clavier
10.8Changements 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.

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.