Skip to Content

Chapitre 4.2 — Ordre & focus visible

⏱️ TL;DR — Deux exigences indissociables. L’ordre du focus suit le DOM : quand tu tabules, tu parcours les éléments dans l’ordre du HTML, pas dans l’ordre visuel — donc si le CSS (order, positionnement) déplace visuellement les choses, l’ordre de tabulation peut devenir illogique (2.4.3). Et le focus doit rester visible : on ne fait jamais outline: none sans remplacement (2.4.7), on préfère :focus-visible, et le focus ne doit pas se cacher derrière un header collant (2.4.11, WCAG 2.2).

🎯 Objectifs

  • Comprendre que l’ordre du focus = l’ordre du DOM, et repérer les pièges du CSS.
  • Garantir un ordre logique conforme à 2.4.3 (Parcours du focus).
  • Ne jamais supprimer l’indicateur de focus sans le remplacer (2.4.7).
  • Utiliser :focus-visible et styliser un focus correct.
  • Éviter que le focus soit masqué par un élément collant (2.4.11, WCAG 2.2).

L’ordre du focus suit le DOM

Quand tu appuies sur Tab, le navigateur passe au prochain élément focusable dans l’ordre du document HTML. Pas dans l’ordre où les choses apparaissent à l’écran : dans l’ordre du DOM. Tant que ton HTML est écrit dans l’ordre logique de lecture, tout va bien — c’est le comportement par défaut, et c’est gratuit.

Le critère WCAG 2.4.3 (Parcours du focus), niveau A, exige que cet ordre préserve le sens et l’opérabilité. Traduction : l’ordre de tabulation doit correspondre à ce que l’utilisateur attend en lisant la page.

Le danger vient du CSS qui réordonne visuellement sans toucher au DOM. Le focus, lui, reste sur l’ordre HTML. Résultat : à l’œil, le bouton « Précédent » est à gauche et « Suivant » à droite ; au clavier, on tombe sur « Suivant » avant « Précédent ». Déroutant.

<!-- DOM : Suivant vient AVANT Précédent --> <div class="pagination"> <button type="button">Suivant</button> <button type="button">Précédent</button> </div>
/* ❌ Le CSS inverse l'ordre VISUEL, mais pas l'ordre du focus */ .pagination { display: flex; } .pagination button:first-child { order: 2; } /* « Suivant » affiché à droite */ .pagination button:last-child { order: 1; } /* « Précédent » affiché à gauche */

À l’écran, tout paraît normal (Précédent à gauche, Suivant à droite). Au clavier, l’utilisateur tabule sur « Suivant » d’abord : l’ordre visuel et l’ordre du focus divergent.

<!-- ✅ Corrige l'ORDRE DU DOM, pas seulement le rendu --> <div class="pagination"> <button type="button">Précédent</button> <button type="button">Suivant</button> </div>
.pagination { display: flex; } /* l'ordre visuel = l'ordre du DOM, tout concorde */

⚠️ Piège — Les propriétés order (flexbox), grid-template-areas / grid-row/grid-column, float, et le positionnement absolu peuvent désynchroniser l’ordre visuel de l’ordre du DOM. Le lecteur d’écran et le Tab suivent le DOM ; l’œil suit le CSS. Quand les deux divergent, l’expérience clavier devient incompréhensible. Corrige l’ordre du HTML, n’utilise pas le CSS pour « ranger » ce qui est mal ordonné dans le DOM.

Le focus doit être visible

Le critère WCAG 2.4.7 (Visibilité du focus), niveau AA, exige un indicateur de focus visible. Concrètement : quand un élément a le focus au clavier, on doit voir lequel. Le navigateur fournit cet indicateur gratuitement — le fameux contour (outline). Le problème, c’est que des générations de développeurs l’ont supprimé parce qu’ils le trouvaient « moche ».

/* ❌❌ Le crime le plus courant de l'a11y web : le focus devient INVISIBLE */ *:focus { outline: none; } button:focus { outline: 0; }

Avec ça, l’utilisateur clavier navigue à l’aveugle : il ne sait plus où il se trouve dans la page. C’est une violation directe de 2.4.7, et l’un des bugs les plus fréquents du web.

La règle absolue : si tu enlèves l’outline, tu le remplaces. Par un outline à toi, un box-shadow, une bordure — quelque chose de nettement visible.

/* ✅ On garde un focus bien visible, juste stylé à notre goût */ :focus-visible { outline: 3px solid #1a5cff; outline-offset: 2px; border-radius: 2px; }

:focus vs :focus-visible

Historiquement, styliser :focus posait un dilemme : le contour apparaissait aussi au clic souris, ce que beaucoup jugeaient disgracieux — d’où la tentation de tout supprimer. La pseudo-classe :focus-visible résout ça : le navigateur n’affiche l’indicateur que quand c’est pertinent, typiquement lors d’une navigation au clavier, pas au simple clic souris sur un bouton.

Le bon réflexe moderne : styliser :focus-visible (et non :focus), avec un indicateur franc. Tu obtiens un focus clavier visible sans le contour « au clic » qui gênait les designers.

/* ✅ Combo robuste : focus visible au clavier, contour discret au clic */ :focus:not(:focus-visible) { outline: none; /* on peut retirer le contour au clic souris... */ } :focus-visible { outline: 3px solid #1a5cff; /* ...mais on le garde, net, au clavier */ outline-offset: 2px; }

💡 Réflexe — Ne raisonne jamais « je supprime le focus ». Raisonne « je redessine le focus pour qu’il colle à ma charte, en gardant un contraste fort ». Un indicateur de focus, c’est comme un curseur de souris pour l’utilisateur clavier : le lui retirer, c’est le rendre aveugle à sa propre position.

Le focus ne doit pas être masqué

Nouveau critère de WCAG 2.2 : 2.4.11 (Focus non masqué, minimum), niveau AA. L’élément qui reçoit le focus ne doit pas être entièrement caché par un autre contenu de la page. Le coupable classique : le header collant (position: sticky / fixed). Quand tu tabules vers un élément situé juste sous le header, le navigateur fait défiler la page pour l’amener en haut… derrière le header, qui le recouvre. L’utilisateur clavier ne voit plus rien.

/* ✅ scroll-margin-top réserve la hauteur du header collant lors du scroll de focus */ :target, a, button, input, select, textarea, [tabindex] { scroll-margin-top: 5rem; /* = hauteur du header sticky, l'élément focalisé reste visible */ }

La propriété scroll-margin-top indique au navigateur de laisser un espace en haut quand il fait défiler vers un élément : le focus arrive sous le header, pleinement visible. Une autre approche est de rendre le header non collant au clavier, mais scroll-margin-top est la solution la plus simple et robuste.

🧭 Sur A11yLearn — Sur A11yLearn, le bandeau de progression du cours est en position: sticky en haut de l’écran. Sans réglage, tabuler vers le premier lien d’une leçon cachait ce lien derrière le bandeau : le focus « disparaissait » visuellement (violation de 2.4.11). On a ajouté scroll-margin-top égal à la hauteur du bandeau sur les éléments focusables : désormais, chaque élément focalisé s’arrête pile sous le bandeau, toujours à l’écran. Au passage, on a remplacé un vieux *:focus { outline: 0 } hérité par un :focus-visible bleu à fort contraste.

✏️ Exercices

Exercice 1 — Répare le focus invisible. Une feuille de styles contient ceci. Explique le problème et corrige-le sans réintroduire de contour « au clic souris » disgracieux.

a:focus, button:focus { outline: none; }

✅ Solution

Le problème : l’indicateur de focus est supprimé, donc l’utilisateur clavier ne voit plus où il est — violation de 2.4.7. On remplace par un focus visible au clavier grâce à :focus-visible :

a:focus-visible, button:focus-visible { outline: 3px solid #1a5cff; outline-offset: 2px; }

:focus-visible n’affiche le contour que lors d’une interaction clavier, pas au clic souris : on satisfait le critère sans le contour « au clic » qui dérangeait le designer.

Exercice 2 — Repère la désynchronisation. Sur une grille de cartes, le designer a utilisé order en CSS pour afficher la carte « Mise en avant » en premier, alors qu’elle est en dernier dans le HTML. Quel critère est menacé, et que corriges-tu ?

✅ Solution

Le critère menacé est 2.4.3 (Parcours du focus) : l’ordre visuel (carte mise en avant en premier) et l’ordre du Tab (elle vient en dernier, comme dans le DOM) divergent. L’utilisateur clavier atteint la carte « en premier » visuellement… en dernier au clavier. Correctif : replacer la carte en premier dans le HTML, et retirer le order CSS. On aligne l’ordre du DOM sur l’ordre visuel voulu, au lieu de tricher avec le CSS.

🧠 Quiz de révision

1. L’ordre du focus suit-il l’ordre visuel ou l’ordre du DOM ?

L’ordre du DOM (l’ordre du HTML). Le Tab ignore la mise en page visuelle. Si le CSS (order, positionnement) réordonne l’affichage sans toucher au DOM, l’ordre du focus et l’ordre visuel divergent — au risque de violer 2.4.3.

2. Que dit le critère 2.4.7 ?

Que l’indicateur de focus doit être visible : au clavier, on doit voir quel élément est focalisé. Supprimer l’outline sans le remplacer viole ce critère de niveau AA.

3. Pourquoi préférer :focus-visible à :focus ?

Parce que :focus-visible n’affiche l’indicateur que lorsque c’est pertinent (navigation clavier), pas au simple clic souris. On obtient donc un focus clavier bien visible sans le contour « au clic » qui poussait tant de développeurs à tout supprimer.

4. Quel est le danger d’un header en position sticky pour le focus ?

Il peut masquer l’élément qui vient de recevoir le focus : en tabulant, la page défile pour amener l’élément en haut… derrière le header collant. C’est la cible du critère 2.4.11 (Focus non masqué) de WCAG 2.2. On corrige avec scroll-margin-top égal à la hauteur du header.

5. Que faut-il toujours faire si on retire l’outline par défaut ?

Le remplacer par un indicateur de focus nettement visible (outline personnalisé, box-shadow, bordure) à fort contraste. Retirer l’outline sans remplacement rend l’utilisateur clavier aveugle à sa position — violation de 2.4.7.


Chapitre suivant : Bien utiliser tabindex0, -1, et pourquoi les valeurs positives sont un anti-pattern.

Last updated on