Un focus clavier invisible peut rendre une interface très difficile, voire impossible, à utiliser pour une personne voyante qui navigue sans souris : l'utilisateur appuie sur Tab, le focus se déplace, mais rien ne signale visuellement sa position. Voici comment diagnostiquer et corriger ce défaut.
Reproduire le problème en dix secondes
Ouvrez n'importe quelle page de votre site, cliquez une fois dans une zone vide en haut, puis appuyez sur Tab de façon répétée. Vous devez voir, à chaque appui, un indicateur visuel se poser sur l'élément suivant : lien, bouton, champ, onglet, élément de menu.
Si aucun indicateur n'est visible sur un élément qui reçoit le focus, le critère RGAA 10.7 n'est pas respecté. Sa méthodologie demande aussi que l'indication soit suffisamment contrastée, avec un rapport d'au moins 3:1 dans les conditions prévues par le test.
Pourquoi c'est bloquant, et pas cosmétique
Une personne voyante qui navigue au clavier a besoin de cet indicateur pour savoir quel contrôle sera activé. Cela concerne notamment des personnes ayant un handicap moteur, des utilisateurs de commandes vocales ou de contacteurs, et toute personne qui utilise le clavier.
Une cause très fréquente
Le coupable est souvent une déclaration qui supprime le contour natif du navigateur sans fournir d'indicateur de remplacement :
- *:focus { outline: none; }- a:focus, button:focus { outline: 0; }
Cette règle peut venir d'un ancien reset CSS, d'un thème, d'un framework ou d'un correctif ajouté pour supprimer le contour qui apparaissait au clic de souris. Dans tous les cas, elle ne doit pas rester sans indicateur de remplacement.
La correction : distinguer le clic du clavier
La pseudo-classe :focus correspond à tout élément focalisé, quelle que soit la manière dont il a reçu le focus. :focus-visible correspond aux situations où le navigateur estime qu'un indicateur doit être affiché, typiquement lors d'une navigation au clavier. Elle permet donc de fournir un indicateur adapté sans supposer que tout focus obtenu au pointeur doit être masqué.
- *:focus { outline: none; } + :focus-visible {+ outline: 2px solid #556BE2;+ outline-offset: 3px;+ border-radius: 4px;+ }
Trois détails comptent dans ce bloc. L'épaisseur du contour doit rester perceptible, un trait d'un pixel se perd sur les fonds clairs. Le décalage évite que le contour se colle au texte et devienne illisible. Et la couleur choisie doit trancher sur les fonds sur lesquels l'élément apparaît, ce qui suppose de la tester sur le fond clair comme sur le fond sombre du site.
Le piège du outline:none qu'on croit avoir supprimé
Après avoir retiré la règle globale, retestez : le focus reste parfois invisible sur certains composants. Deux explications.
La première est une déclaration plus spécifique restée en place plus bas dans la feuille, ou dans le CSS d'un composant. Cherchez outline dans l'ensemble du projet, pas seulement dans le reset.
La seconde est plus sournoise : le contour existe mais il est masqué. Un parent avec overflow: hidden peut découper aussi bien un contour extérieur qu'une ombre portée extérieure. Corrigez si possible la règle d'overflow ; sinon, placez l'indicateur à l'intérieur du composant avec un décalage négatif ou une ombre interne, puis vérifiez son contraste.
+ .card a:focus-visible {+ outline: 3px solid #556BE2;+ outline-offset: -3px;+ }
Neutraliser le contour est acceptable si, et seulement si, un autre indicateur visible le remplace.
Les composants faits maison
Les éléments interactifs natifs reçoivent le focus tout seuls. Les faux boutons construits en div ou en span, non. Ils sont invisibles au clavier et le resteront quel que soit le CSS.
La bonne correction n'est pas d'ajouter un tabindex par dessus, c'est de revenir à l'élément natif. Un button apporte gratuitement le focus, l'activation à la barre d'espace et à Entrée, et le rôle correct pour les technologies d'assistance. Tout cela est à reconstruire à la main dans le cas contraire.
- <div class="btn" onclick="valider()">Valider</div>+ <button type="button" class="btn" onclick="valider()">Valider</button>
Quand l'élément natif ne peut réellement pas être utilisé, il faut rendre le composant focalisable, exposer le rôle, le nom, les états et propriétés appropriés, puis reproduire en JavaScript toutes les interactions clavier attendues par le motif ARIA correspondant. Ajouter seulement role et tabindex="0" ne suffit pas.
Le cas du focus caché sous un header sticky
Un en-tête sticky ou fixe peut recouvrir un élément lorsque celui-ci reçoit le focus. WCAG 2.2 traite ce cas avec le critère 2.4.11 « Focus non masqué (minimum) », de niveau AA : le composant focalisé ne doit pas être entièrement caché par un contenu créé par l'auteur. Ce critère ne fait pas partie du RGAA 4.1.2, fondé sur WCAG 2.1, mais il constitue une exigence WCAG 2.2 à anticiper.
Une technique reconnue par le W3C consiste à réserver, dans le conteneur qui défile, un espace correspondant à la hauteur de l'en-tête :
+ html {+ scroll-padding-top: 96px; /* hauteur du header */+ }
Vérifier, dans l'ordre
- Parcourez chaque gabarit entièrement au clavier, Tab puis Maj+Tab, sans jamais toucher la souris.
- Vérifiez que l'ordre de tabulation suit l'ordre visuel de lecture, sans saut incohérent.
- Ouvrez et fermez chaque composant à état : menu déroulant, fenêtre modale, accordéon, onglets.
- Contrôlez qu'aucun piège au clavier n'existe, autrement dit que l'on peut toujours ressortir d'un composant sans la souris.
- Refaites le tour avec le zoom du navigateur poussé, où les en-têtes collants prennent plus de place.
Un contrôle automatisé peut signaler certaines règles qui suppriment l'indicateur de focus et certains éléments interactifs non focalisables. Il ne peut pas garantir que l'indicateur reste visible et contrasté sur tous les fonds, que l'ordre de tabulation est cohérent ni que chaque composant se comporte correctement. Le parcours manuel au clavier reste donc indispensable.

