vendredi 25 septembre 2026

À propos

Contact

Accessibilité

Construire un accordéon accessible au clavier sans bibliothèque JavaScript

Un accordéon de FAQ mal codé ignore le clavier et les lecteurs d'écran. Voici comment en construire un, entièrement en JavaScript natif, en suivant le motif ARIA officiel.

Par Clément Hadrot • 25 février 2020 • 4 min de lecture • Aucun commentaire
Construire un accordéon accessible au clavier sans bibliothèque JavaScript

Un client m’a un jour envoyé une capture d’écran de sa FAQ : douze questions empilées dans un accordéon acheté sur une place de marché de plugins. Au clavier, impossible d’ouvrir la moindre réponse — seul le clic à la souris fonctionnait. Le plugin avait été codé avec des <div> cliquables et aucun gestionnaire de touche. Plutôt que de chercher une alternative payante, nous avons reconstruit l’accordéon à la main, sans bibliothèque, en suivant le motif de conception publié par le W3C.

La bonne nouvelle : un accordéon accessible ne demande ni React ni jQuery. Il tient dans un HTML sobre, un peu de CSS et une soixantaine de lignes de JavaScript natif. La difficulté n’est pas la quantité de code mais l’exactitude des attributs ARIA et de la gestion du focus.

Le HTML de départ

Chaque en-tête d’accordéon est un vrai <button>, jamais un <div> ni un <span> : c’est ce qui donne gratuitement le focus clavier, l’activation par Entrée et Espace, et le rôle button.

<h3>
  <button type="button" class="accordion-trigger" aria-expanded="false" aria-controls="panel-livraison">
    Quels sont les délais de livraison ?
  </button>
</h3>
<div id="panel-livraison" class="accordion-panel" role="region" aria-labelledby="panel-livraison-label" hidden>
  <p>Les commandes passées avant 14 h sont expédiées le jour même.</p>
</div>

L’attribut aria-expanded reflète l’état ouvert ou fermé du panneau associé, désigné par aria-controls. L’attribut hidden masque le contenu du panneau à tout le monde, y compris aux lecteurs d’écran, tant qu’il n’est pas ouvert — c’est plus fiable qu’un simple display: none posé en CSS seul, car il reste vrai même si une feuille de style tarde à charger.

Le script : ouverture, fermeture, navigation entre en-têtes

L'essentiel à retenir : aria-expanded sur chaque bouton ; Flèches haut/bas pour naviguer entre en-têtes ; Aucune dépendance, 60 lignes de JS

Le script écoute les clics et les touches sur chaque bouton d’en-tête. Les flèches Haut et Bas déplacent le focus vers l’en-tête précédent ou suivant, comme le préconise le motif ARIA « accordion » :

const triggers = document.querySelectorAll('.accordion-trigger');

triggers.forEach((trigger, index) => {
  trigger.addEventListener('click', () => toggle(trigger));
  trigger.addEventListener('keydown', (event) => {
    if (event.key === 'ArrowDown') {
      event.preventDefault();
      const next = triggers[index + 1] || triggers[0];
      next.focus();
    }
    if (event.key === 'ArrowUp') {
      event.preventDefault();
      const prev = triggers[index - 1] || triggers[triggers.length - 1];
      prev.focus();
    }
  });
});

function toggle(trigger) {
  const expanded = trigger.getAttribute('aria-expanded') === 'true';
  const panel = document.getElementById(trigger.getAttribute('aria-controls'));
  trigger.setAttribute('aria-expanded', String(!expanded));
  panel.hidden = expanded;
}

Ce script suffit pour un accordéon dit « exclusif optionnel » : chaque panneau s’ouvre et se ferme indépendamment, plusieurs peuvent rester ouverts en même temps. Si le besoin est qu’un seul panneau reste ouvert à la fois, il suffit de fermer les autres boutons dans la fonction toggle avant d’ouvrir le nouveau.

Ce qu’il ne faut surtout pas faire

  • Utiliser aria-hidden sur le panneau plutôt que hidden : aria-hidden="true" masque le contenu aux technologies d’assistance, mais un utilisateur de clavier seul (sans lecteur d’écran) pourrait encore tabuler jusqu’à des liens invisibles à l’écran.
  • Animer l’ouverture avec une hauteur calculée en JavaScript sans respecter prefers-reduced-motion : certains utilisateurs souffrent de troubles vestibulaires déclenchés par ces transitions.
  • Rendre les en-têtes focusables au <div tabindex="0"> plutôt qu’à de vrais boutons : le clavier fonctionnera peut-être, mais l’activation par Espace et l’annonce du rôle demanderont un travail supplémentaire inutile.

Intégrer ça dans un contexte WordPress

Dans un thème classique, ce script se charge simplement via wp_enqueue_script() avec l’attribut defer, sans dépendance sur jQuery. Pour un accordéon inséré depuis l’éditeur de blocs, le même script peut être attaché à un bloc personnalisé enregistré avec register_block_type(), le rendu du bloc produisant directement le HTML présenté plus haut.

Pour aller plus loin

Le motif complet, avec ses variantes (accordéon à panneau unique, en-têtes de niveaux de titre différents), est documenté par le ARIA Authoring Practices Guide. C’est la référence à garder sous la main avant d’écrire le moindre composant interactif fait maison, accordéon ou non.

Partager :

À propos de l'auteur

Clément Hadrot

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi