vendredi 25 septembre 2026

À propos

Contact

Accessibilité

Construire un composant select personnalisé accessible au clavier

Remplacer un select natif par un composant sur mesure est tentant pour le design, mais périlleux pour le clavier. Voici comment le construire sans rien casser.

Par Clément Hadrot • 24 juin 2025 • 4 min de lecture • Aucun commentaire
Construire un composant select personnalisé accessible au clavier

Le <select> HTML natif est accessible par défaut, complètement gratuit, et pourtant régulièrement remplacé par un composant maison dès qu’un designer veut des options avec icônes, descriptions multi-lignes ou recherche instantanée. Le problème n’est pas de le remplacer : c’est de le remplacer sans reproduire son contrat d’accessibilité.

Ce tutoriel construit, étape par étape, un composant select personnalisé pour un thème WordPress, avec un vrai support clavier et lecteur d’écran, sans dépendance à une bibliothèque tierce lourde.

Étape 1 — Poser le balisage de base

La structure repose sur le pattern ARIA listbox. Un bouton déclencheur, suivi d’une liste d’options masquée par défaut :

<button type="button" class="select-bouton" aria-haspopup="listbox" aria-expanded="false">
  Choisir un format
</button>
<ul class="select-liste" role="listbox" tabindex="-1" hidden>
  <li role="option" data-valeur="pdf" id="opt-pdf">PDF</li>
  <li role="option" data-valeur="epub" id="opt-epub">EPUB</li>
  <li role="option" data-valeur="audio" id="opt-audio">Livre audio</li>
</ul>

Étape 2 — Ouvrir la liste et déplacer le focus

L'essentiel à retenir : Le rôle listbox reproduit exactement le comportement attendu d'un select natif ; Les flèches haut et bas doivent déplacer le focus virtuel, pas seulement visuel ; Le typeahead au clavier reste attendu par les utilisateurs de lecteur d'écran

À l’activation du bouton (clic ou touche Entrée/Espace), la liste doit s’afficher et recevoir le focus. C’est la liste elle-même qui reste focus, avec un focus visuel géré sur l’option courante via aria-activedescendant plutôt qu’en déplaçant le focus réel sur chaque <li> :

bouton.addEventListener('click', function () {
  var ouvert = liste.hidden === false;
  liste.hidden = ouvert;
  bouton.setAttribute('aria-expanded', String(!ouvert));
  if (!ouvert) {
    liste.focus();
    definirOptionActive(premiereOption());
  }
});

Étape 3 — Gérer les flèches et la sélection

Les flèches haut et bas déplacent l’option active, Entrée ou Espace valide la sélection, Échap referme la liste sans changer la valeur :

liste.addEventListener('keydown', function (event) {
  switch (event.key) {
    case 'ArrowDown':
      event.preventDefault();
      definirOptionActive(optionSuivante());
      break;
    case 'ArrowUp':
      event.preventDefault();
      definirOptionActive(optionPrecedente());
      break;
    case 'Enter':
    case ' ':
      event.preventDefault();
      valider(optionActive());
      break;
    case 'Escape':
      fermerSansValider();
      break;
  }
});

Étape 4 — Ajouter le typeahead

Un utilisateur de lecteur d’écran habitué au <select> natif s’attend à pouvoir taper la première lettre d’une option pour y sauter directement. Sans cette fonctionnalité, le composant paraît cassé même s’il respecte le reste du pattern ARIA :

  • Capturer les frappes de caractères sur la liste ouverte
  • Chercher la première option dont le libellé commence par la lettre tapée
  • Réinitialiser la recherche après un court délai d’inactivité

Étape 5 — Synchroniser avec un champ caché pour le formulaire

Pour que le composant reste utilisable dans un formulaire WordPress classique, la valeur choisie doit être répercutée dans un champ <input type="hidden"> soumis normalement. Cela évite de réécrire toute la logique de traitement côté serveur.

Tester avant de livrer

La validation ne se limite pas à vérifier que la souris fonctionne. Le test minimal consiste à débrancher la souris et à parcourir tout le composant au clavier avec NVDA activé, en vérifiant que chaque option est annoncée avec son état sélectionné.

Un select personnalisé qui ne supporte pas le typeahead n’est pas un détail manquant : c’est un contrat rompu avec l’utilisateur qui connaît déjà le comportement standard.

En résumé

Reconstruire un select accessible demande de reproduire fidèlement trois choses : la navigation aux flèches avec aria-activedescendant, la fermeture cohérente au clavier, et le typeahead. Ce n’est pas plus compliqué qu’un menu déroulant classique, à condition de partir du pattern ARIA listbox plutôt que d’improviser une structure de <div> cliquables.

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