Un piège clavier, c’est une situation où l’utilisateur entre dans une zone de la page et ne peut plus en sortir avec le clavier. Son focus est coincé, il tourne en boucle dans un composant, ou disparaît complètement dans un élément muet.
C’est l’une des erreurs les plus bloquantes en accessibilité. Contrairement à un texte alternatif manquant ou à un contraste insuffisant qui dégradent l’expérience, un piège clavier arrête complètement la navigation. L’utilisateur ne peut plus avancer dans la page sans fermer l’onglet.
Ce qu’est un piège clavier
La définition du RGAA (critère 12.9) est précise : dans chaque page web, la navigation ne doit pas contenir de piège au clavier.
Un piège au clavier existe quand :
- Le focus entre dans un composant (en tabulant ou en cliquant)
- L’utilisateur ne peut pas en sortir avec les touches habituelles (Tab, Maj+Tab, Échap)
- Le seul recours est la souris, le raccourci Alt+F4, ou la fermeture de l’onglet
Les pièges peuvent être absolus (impossible de sortir) ou relatifs (le focus tourne en boucle à l’intérieur d’un composant ouvert sans que l’utilisateur le veuille).
Piège 1 : La modale sans focus trap
Le piège clavier le plus courant et le plus documenté encore très fréquent.
Ce qui se passe
Une modale s’ouvre. L’utilisateur appuie sur Tab pour atteindre les boutons. Mais le focus traverse la modale, passe derrière l’overlay, et visite les liens du contenu en arrière-plan. L’utilisateur tabule dans le vide, les éléments de fond reçoivent le focus, mais ils sont masqués visuellement par la modale.
Parfois le contraire : la modale n’a pas de focus trap intentionnel mais le composant est implémenté de façon à ce que Tab après le dernier bouton n’aille nulle part. Focus perdu.
Le bon comportement
Quand une modale est ouverte :
- Tab et Maj+Tab doivent rester à l’intérieur de la modale
- Échap doit fermer la modale et retourner le focus au déclencheur
- À l’ouverture, le focus doit se déplacer sur le premier élément interactif de la modale (ou sur la modale elle-même si elle a
tabindex="-1").
La note sur inert : l’attribut HTML inert (supporté depuis 2023 dans tous les navigateurs modernes) est une alternative plus élégante. Appliquer inert sur le contenu en arrière-plan bloque automatiquement le focus et la lecture des technologies d’assistance (AT) sans JavaScript complexe.
<main inert>
<!-- contenu de fond inaccessible pendant que la modale est ouverte -->
</main>
<div role="dialog" aria-modal="true" aria-labelledby="titre-modal">
<h2 id="titre-modal">Confirmer</h2>
<button>Oui</button>
<button>Non</button>
</div>
En audit WordPress
Les plugins de modale (FancyBox, Popup Maker, WooCommerce lightbox) gèrent rarement le focus trap correctement. Tester systématiquement : ouvrir la modale au clavier, appuyer sur Tab répétitivement, observer si le focus reste à l’intérieur ou s’échappe.
Piège 2 : Le menu dropdown sans Échap
Ce qui se passe
L’utilisateur atteint un lien de navigation avec un sous-menu. Il appuie sur Entrée ou la flèche bas, le sous-menu s’ouvre. Il visite les liens du sous-menu. Mais comment en sortir sans aller jusqu’au dernier lien ?
Sur un menu natif HTML (pas de JS), Tab sort naturellement du sous-menu vers l’élément suivant dans le DOM. Mais sur un menu custom JavaScript, le comportement dépend entièrement de l’implémentation.
Les deux erreurs fréquentes :
- Échap ne ferme pas le sous-menu : l’utilisateur est coincé à tabuler à travers tous les items.
- Le sous-menu se ferme au blur : dès que le focus quitte le déclencheur, le sous-menu disparaît et le focus retourne au déclencheur, créant une boucle infinie.
Le bon comportement
// Le bouton déclencheur de sous-menu
btnSousMenu.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
// Fermer le sous-menu
sousMenu.hidden = true;
btnSousMenu.setAttribute('aria-expanded', 'false');
// Retourner le focus au déclencheur
btnSousMenu.focus();
}
});
// Les liens du sous-menu
sousMenuLinks.forEach(lien => {
lien.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
sousMenu.hidden = true;
btnSousMenu.setAttribute('aria-expanded', 'false');
btnSousMenu.focus();
}
});
});
Ce qu’on attend :
- Entrée ou Espace sur le déclencheur : ouvre le sous-menu, focus sur le premier item
- Flèche bas / haut : navigation entre les items du sous-menu
- Échap depuis n’importe quel item : ferme le sous-menu, retour au déclencheur
- Tab depuis le dernier item : ferme le sous-menu, passe à l’élément suivant dans la page
En audit WordPress
Le bloc Navigation de WordPress Full Site Editing (FSE) qui utilise l’Interactivity API, gère correctement Échap sur les sous-menus. Les menus générés par des thèmes classiques ou des plugins de méga-menu sont à vérifier systématiquement.
Piège 3 : Le carrousel sans contrôles clavier
Ce qui se passe
Un carrousel avec défilement automatique. L’utilisateur atteint le carrousel par Tab. Les images défilent. Il essaie d’atteindre les boutons “précédent” et “suivant”, soit ils ne reçoivent pas le focus (non focusables), soit le défilement automatique change la diapositive pendant qu’il lit, désynchronisant le contenu annoncé.
La variante la plus grave : un carrousel où Tab entre dans le premier slide puis n’en ressort pas, le focus reste bloqué dans la zone du carrousel, tabulant entre les liens du premier slide à l’infini.
Le bon comportement
<!-- Les contrôles sont des <button> natifs — focusables par défaut -->
<section aria-label="Actualités" aria-roledescription="carrousel">
<!-- Contrôle de défilement automatique EN PREMIER dans le DOM -->
<!-- → L'utilisateur peut le désactiver avant d'entrer dans le contenu -->
<button type="button" aria-label="Mettre en pause le défilement automatique"
id="btn-pause">
⏸
</button>
<div aria-live="polite" aria-atomic="false" class="piste">
<div role="group" aria-roledescription="diapositive" aria-label="1 sur 4">
<a href="/article-1">Titre de l'actualité 1</a>
</div>
<!-- autres slides, aria-hidden="true" sur les non-actives -->
</div>
<button type="button" aria-label="Diapositive précédente">←</button>
<button type="button" aria-label="Diapositive suivante">→</button>
</section>
Ce qu’on attend :
- Tab atteint le bouton pause en premier
- Tab atteint les boutons précédent/suivant
- Tab traverse normalement le contenu de la diapositive active
- Tab sort du carrousel vers l’élément suivant dans la page
- Aucun piège, aucune boucle
La règle prefers-reduced-motion :
@media (prefers-reduced-motion: reduce) {
.carrousel-auto { animation: none; }
}
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
clearInterval(intervalDefilement);
btnPause.hidden = true; // inutile si le défilement est désactivé
}
En audit WordPress
Les plugins de carrousel (Easing Slider, Smart Slider, WP Slick) ont un bilan très variable. Le test à faire : entrer dans le carrousel par Tab, vérifier qu’on peut en sortir par Tab, vérifier qu’Échap ou un bouton pause arrête le défilement automatique.
Piège 4 : L’overlay qui laisse le fond focusable
Différent de la modale, c’est le cas des overlays de cookie, des bandeaux de notification, des panneaux latéraux qui apparaissent par-dessus le contenu sans bloquer le fond.
Ce qui se passe
Un bandeau de consentement cookies apparaît en bas de page. L’utilisateur commence à tabuler. Son focus va dans l’overlay, puis ressort dans la page principale derrière, puis revient dans l’overlay, etc. Le focus et l’overlay sont désynchronisés.
Ou pire : l’overlay apparaît au chargement et capture le focus du navigateur, qui se retrouve placé sur un élément invisible (le body ou un div sans label), sans annonce AT.
Le bon comportement
Si l’overlay est critique (bandeau cookies) :
// À l'apparition de l'overlay
document.body.setAttribute('inert', ''); // rendre tout le fond inerte
overlay.removeAttribute('inert');
overlay.querySelector('button, a, [tabindex]')?.focus(); // focus dans l'overlay
// À la fermeture
document.body.removeAttribute('inert');
// Retour au focus précédent ou début de page
Si l’overlay est secondaire (notification non bloquante) :
<!-- La notification est en aria-live, annoncée sans capturer le focus -->
<div role="status" aria-live="polite" class="notification">
Vos préférences ont été enregistrées.
</div>
Protocole de test en cinq minutes
Ce protocole permet de détecter les pièges clavier les plus courants sur n’importe quelle page.
Matériel : un navigateur, pas de souris.
Étape 1 : Navigation générale (1 min)
Cliquer dans la barre d’adresse. Appuyer sur Tab 15 à 20 fois. Observer :
- Le focus est-il visible à chaque étape ?
- L’ordre est-il logique (haut → bas, gauche → droite) ?
- Y a-t-il des éléments qui semblent focusables mais ne reçoivent jamais le focus ?
Étape 2 : Tester les composants interactifs (2 min)
Pour chaque composant interactif (menu, accordéon, modale, carrousel) :
- L’atteindre par Tab
- L’activer (Entrée ou Espace)
- Appuyer sur Échap → le composant se ferme et le focus revient au déclencheur ?
- Tabuler à l’intérieur → le focus reste dans le composant ou s’échappe ?
- Tabuler après le dernier élément du composant → le focus sort vers la suite de la page ?
Étape 3 : Tester les formulaires (1 min)
Atteindre chaque champ de formulaire par Tab. Remplir. Soumettre avec Entrée.
- Y a-t-il des champs jamais atteints ?
- Les erreurs sont-elles annoncées et le focus se déplace-t-il sur le champ en erreur ?
Étape 4 : Tester les overlays (30 s)
Si un bandeau cookies ou une notification apparaît :
- Le focus entre-t-il dans le bandeau ?
- Peut-on le fermer au clavier ?
- Le focus revient-il au bon endroit après fermeture ?
Étape 5 : Vérifier la sortie (30 s)
Naviguer jusqu’en bas de la page par Tab. Appuyer sur Maj+Tab pour remonter. L’ordre inverse fonctionne-t-il également ?
Ce que RGAA exige
Critère 12.9 : Dans chaque page web, la navigation ne doit pas contenir de piège au clavier.
Un piège clavier est une non-conformité de niveau A, le niveau le plus critique dans la hiérarchie WCAG. C’est un problème qui rend la page inutilisable pour l’utilisateur clavier, pas simplement difficile.
En pratique, un site avec des pièges clavier ne peut pas prétendre à la conformité RGAA, quel que soit le score sur les autres critères.
Critère 7.3 : Chaque script est-il contrôlable par le clavier et par tout dispositif de pointage ?
Complémentaire au 12.9 : non seulement la navigation ne doit pas créer de pièges, mais chaque composant JavaScript doit être entièrement utilisable au clavier. Atteindre un composant sans pouvoir l’activer est une NC sur 7.3. Y entrer sans pouvoir en sortir est une NC sur 12.9.