Le WordPress d'aujourd'hui, décodé pour les développeurs

Accessibilité

Construire un mega-menu accessible pour un site de recrutement

Un snippet complet pour un mega-menu de catégories de métiers, navigable aux flèches et fermé à l'Échap, sans bibliothèque tierce.

Par WordPress Développement • 21 octobre 2023 • 5 min de lecture • Aucun commentaire
Construire un mega-menu accessible pour un site de recrutement

Comment un candidat qui navigue au clavier retrouve-t-il la catégorie « développement » dans un mega-menu de vingt métiers rangés en quatre colonnes ? C’est la question posée par un cabinet de recrutement qui refondait son site vitrine sous WordPress et voulait un menu de catégories professionnelles sans plugin de navigation.

Le brief tenait en une phrase : chaque colonne du mega-menu regroupe une famille de métiers (tech, marketing, finance, opérations), et un visiteur au clavier doit pouvoir circuler entre les liens avec les flèches, sans jamais perdre le focus ni tomber dans une impasse.

Le problème : un menu à plat ne suffit pas

Un mega-menu HTML classique, construit avec des <ul> imbriqués, fonctionne déjà à la tabulation : chaque lien reçoit le focus dans l’ordre du DOM. Mais avec vingt liens répartis sur quatre colonnes, appuyer vingt fois sur Tab pour atteindre la dernière colonne décourage n’importe quel utilisateur. Il fallait donc ajouter une navigation par flèches, calquée sur le modèle grid décrit par les pratiques ARIA de la WAI, sans pour autant transformer le menu en widget complexe et fragile.

Deuxième contrainte : la fermeture. Un mega-menu ouvert au clic ou au focus doit se refermer proprement à la touche Échap, en renvoyant le focus sur le bouton qui l’a ouvert. Beaucoup d’implémentations maison oublient cette étape et laissent l’utilisateur clavier coincé dans un menu invisible.

L'essentiel à retenir : Flèches directionnelles entre colonnes ; Échap referme et rend le focus ; Aucune dépendance JavaScript externe

Le snippet : structure HTML et rôles

La structure repose sur un bouton natif suivi d’un conteneur de menu. Aucun rôle exotique n’est nécessaire : un <button> avec aria-expanded et aria-controls, et un conteneur classique.

<button id="menu-metiers-btn" aria-expanded="false" aria-controls="menu-metiers">
  Nos métiers
</button>
<div id="menu-metiers" class="mega-menu" hidden>
  <ul class="mega-menu__colonne">
    <li><a href="/metiers/developpement">Développement</a></li>
    <li><a href="/metiers/qa">Qualité logicielle</a></li>
  </ul>
  <ul class="mega-menu__colonne">
    <li><a href="/metiers/marketing">Marketing</a></li>
    <li><a href="/metiers/communication">Communication</a></li>
  </ul>
</div>

Le comportement d’ouverture bascule l’attribut hidden et met à jour aria-expanded : rien de plus, aucun rôle menu ou menuitem, qui imposeraient une gestion du focus beaucoup plus lourde et souvent mal maîtrisée.

La navigation aux flèches, ligne par ligne

Le script écoute les événements clavier sur le conteneur et calcule la position du lien focalisé dans une grille virtuelle construite à partir des colonnes réelles du DOM.

const liens = [...menu.querySelectorAll('a')];
menu.addEventListener('keydown', (e) => {
  const index = liens.indexOf(document.activeElement);
  if (index === -1) return;
  if (e.key === 'ArrowDown') {
    e.preventDefault();
    liens[(index + 1) % liens.length].focus();
  }
  if (e.key === 'ArrowUp') {
    e.preventDefault();
    liens[(index - 1 + liens.length) % liens.length].focus();
  }
  if (e.key === 'Escape') {
    e.preventDefault();
    fermerMenu();
    document.getElementById('menu-metiers-btn').focus();
  }
});

Les flèches gauche et droite suivent le même principe, mais en sautant d’une colonne à l’autre plutôt que d’un lien au suivant : on additionne la taille d’une colonne à l’index courant. Ce calcul reste lisible parce que chaque colonne compte le même nombre approximatif de métiers ; avec des colonnes très déséquilibrées, mieux vaut stocker les index de colonne directement en attribut data-colonne sur chaque lien.

Variantes selon le contexte

  • Sur un thème basé sur les blocs, le mega-menu peut être injecté via un bloc personnalisé enregistré avec register_block_type(), ce qui permet aux rédacteurs de réordonner les colonnes de métiers sans toucher au code.
  • Sur un site multilingue, chaque lien de métier doit garder son intitulé traduit dans l’attribut lang si la langue de la page cible diffère de la langue courante.
  • Pour un menu à une seule colonne, la navigation par flèches redevient une simple liste verticale : autant simplifier le script plutôt que de forcer une logique de grille inutile.

Un point souvent négligé : le survol à la souris

Un mega-menu qui ne s’ouvre qu’au survol pose un problème réel aux utilisateurs de trackpad ou de commande vocale : le menu peut s’ouvrir sans intention et masquer le contenu en dessous. La solution retenue ici combine clic et focus comme déclencheurs, et ignore volontairement le survol seul.

Un menu qui s’ouvre au survol et se comporte différemment au clavier n’est pas un menu accessible avec un bonus clavier : c’est deux menus qui doivent rester synchronisés, et qui finissent toujours par diverger après la troisième mise à jour du thème.

En résumé

Sept rôles et attributs ARIA (aria-expanded, aria-controls, hidden, et les gestionnaires de flèches et d’Échap) suffisent à rendre un mega-menu de recrutement pleinement utilisable au clavier, sans framework de composants ni bibliothèque de menu. Le vrai travail se situe dans les tests manuels : ouvrir, naviguer aux flèches jusqu’à la dernière colonne, refermer à l’Échap, vérifier que le focus revient bien sur le bouton d’origine. Ces quatre étapes, répétées à chaque modification du menu, évitent la régression silencieuse la plus fréquente sur ce type de composant.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Partager :

À propos de l'auteur

WordPress Développement

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

Voir tous ses articles

Dans la même veine

À lire aussi