Un client personnalisant lui-même les couleurs d’accentuation de son thème via un réglage maison ajouté au Customizer avait signalé un blocage complet : une fois le sélecteur de couleurs ouvert, impossible de le refermer sans réactualiser la page. Ce tutoriel construit, étape par étape, un sélecteur de couleurs accessible qui évite ce piège, sans revenir sur la vérification du contraste des couleurs une fois choisies, déjà traitée dans un autre article.
Étape 1 : identifier la structure du composant
Le sélecteur affiche une grille de pastilles de couleur prédéfinies, plus un champ de saisie libre au format hexadécimal, dans un panneau flottant ouvert au clic sur un bouton d’aperçu de la couleur actuelle. Cette structure correspond à un motif ARIA reconnu, proche d’une combobox ou d’un menu déroulant complexe, ce qui impose des règles précises de gestion du clavier, faute de quoi le composant devient soit invisible pour un lecteur d’écran, soit littéralement bloquant.
Étape 2 : ouvrir le panneau avec un état correctement annoncé
Le bouton qui ouvre le panneau doit porter aria-haspopup="true" et un état aria-expanded qui bascule entre "false" et "true" à l’ouverture :
<button
type="button"
aria-haspopup="true"
aria-expanded="false"
aria-label="Choisir la couleur d'accentuation, actuellement bleu marine">
<span class="apercu-couleur" style="background-color:#1e3a8a"></span>
</button>
Étape 3 : la faute qui créait le piège
Le composant initial capturait tous les événements keydown du document dès l’ouverture du panneau, pour permettre de naviguer entre les pastilles avec les flèches du clavier. Le problème : ce gestionnaire interceptait aussi la touche Échap, mais sans jamais appeler la fonction de fermeture — un oubli pur et simple dans le code d’origine, qui laissait la personne coincée dans le panneau sans aucune sortie clavier possible.

// Avant : Échap capturée mais jamais traitée
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowRight') deplacerSelectionA(1);
if (e.key === 'ArrowLeft') deplacerSelectionA(-1);
// aucune ligne pour 'Escape' : le piège vient de là
});
// Après : Échap ferme systématiquement et restitue le focus
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowRight') deplacerSelectionA(1);
if (e.key === 'ArrowLeft') deplacerSelectionA(-1);
if (e.key === 'Escape') {
fermerPanneau();
boutonDeclencheur.focus();
}
});
Étape 4 : rendre les pastilles navigables aux flèches, pas seulement à Tab
Une grille de couleurs se navigue naturellement aux flèches directionnelles, pas en tabulant pastille par pastille, ce qui serait à la fois lent et contre-intuitif pour qui connaît déjà ce type de composant. La grille utilise donc un seul élément dans l’ordre de tabulation (tabindex="0" sur la pastille sélectionnée, tabindex="-1" sur les autres), et les flèches déplacent le focus logique à l’intérieur de la grille :
Flèche droite / gauche: pastille suivante ou précédente sur la même ligne.Flèche haut / bas: pastille équivalente sur la ligne au-dessus ou en dessous.EntréeouEspace: valide la couleur sélectionnée et ferme le panneau.Échap: ferme le panneau sans valider, restitue le focus au bouton déclencheur.
Étape 5 : vérifier le champ de saisie hexadécimale en parallèle
Le champ de saisie libre, pour une couleur hors de la grille prédéfinie, doit rester un champ de formulaire standard avec un <label> explicite (« Couleur personnalisée, format hexadécimal ») et ne doit jamais être capturé par le même gestionnaire de flèches que la grille, sous peine d’empêcher tout déplacement du curseur de texte pendant la saisie — un second piège plus discret rencontré pendant les tests de ce composant.
Étape 6 : tester le cycle complet d’ouverture et de fermeture
Le test final consiste à ouvrir le panneau au clavier, naviguer dans la grille aux flèches, valider une couleur avec Entrée, puis rouvrir le panneau et le fermer cette fois avec Échap sans valider, en vérifiant à chaque fois que le focus revient précisément sur le bouton déclencheur, jamais sur body ni ailleurs dans la page.
Un piège de focus n’est presque jamais voulu : il vient d’un gestionnaire d’événements qui capture une touche sans prévoir tous les cas de sortie. La checklist la plus utile reste simple : chaque fois qu’un composant capture
keydownglobalement, vérifier explicitement queÉchapy est traitée.
En résumé
Ce sélecteur de couleurs, bloquant à l’origine par un simple oubli de gestion de la touche Échap, est devenu, après correction, un exemple réutilisable de grille de couleurs accessible : ouverture annoncée par aria-expanded, navigation aux flèches à l’intérieur de la grille, et sortie garantie par Échap avec restitution du focus. Le contraste des couleurs elles-mêmes reste un sujet distinct, déjà traité par ailleurs.