RGAA Lab

Composants accessibles

Les liens d'évitement

Le lien d'évitement est le premier élément focalisable de la page, invisible jusqu'à ce qu'on le tabule. Il ne vaut que si sa cible peut recevoir le focus. Démo, code, critères RGAA.

4 critères RGAA en jeu

Essayez-les

Le rapport d’audit

Un titre ne reçoit pas le focus. Un lien qui le vise fait défiler la page jusqu’à lui et s’arrête là.

Un lien, pour voir où mène la tabulation suivante

Activez l’un des deux liens d’évitement pour voir où le focus atterrit.

Posez le focus dans la page miniature et tabulez : deux liens d’évitement apparaissent avant le menu. Activez le premier, puis appuyez sur

Tab — le rapport indique la zone de contenu, puis le lien qui s'y trouve. Remontez en Maj + Tab et recommencez avec le second : la page défile au même endroit et le Tab suivant mène au même lien, mais le rapport indique cette fois **le corps du document** — rien ne porte le focus. Seule leur cible diffère, et le rapport dit laquelle l'a reçu.

Cette démo existe aussi en page autonome.

L’erreur courante

Le lien d’évitement écrit une fois, jamais essayé au clavier.

<!-- À ne pas faire -->
<a class="evitement" href="#contenu">Aller au contenu</a><main id="contenu"></main>
/* À ne pas faire : ce qui n'est pas affiché ne prend pas le focus */
.evitement {
	display: none;
}
.evitement:focus {
	display: block;
}

Deux fautes, et la seconde est celle que personne ne voit.

display: none retire l’élément de l’ordre de tabulation. Le :focus qui suit ne se déclenchera donc jamais : pour qu’il s’applique, il faudrait que le lien puisse recevoir le focus, ce que la règle précédente lui interdit. Le lien est dans le code, il n’existe pour personne. visibility: hidden et l’attribut hidden produisent exactement le même résultat.

Mais même corrigé, ce lien reste à moitié cassé, parce que <main> ne peut pas recevoir le focus. Le navigateur fait défiler la page et pose sur la cible le point de départ de la tabulation : le Tab suivant continue bien dans le contenu. Mais aucun élément ne porte le focus — document.activeElement retombe sur body. Rien n’est annoncé, aucun indicateur de focus n’est visible, et le point de départ se perd dès qu’un script re-rend la zone ou déplace le focus. Le glossaire exige de reprendre « la lecture et la navigation clavier » : sans tabindex="-1", seule la seconde moitié est acquise.

Trois variantes de la même méprise :

  • La cible est un titre : href="#titre-page" vers un <h1 id="titre-page">. Un titre n’est pas focalisable ; c’est le cas le plus fréquent, parce qu’il paraît le plus naturel.
  • tabindex="0" sur la cible au lieu de -1 : le contenu principal devient un arrêt de tabulation pour tout le monde, sur toutes les pages, sans que personne n’ait rien demandé.
  • Le lien réutilise la classe sr-only, ou se réduit à width: 0; height: 0, sans neutralisation au focus : il est bien focalisable — un élément de dimension nulle reste dans l’ordre de tabulation —, mais reste invisible une fois atteint. À la souris, personne ne le remarque ; au clavier, le focus disparaît de l’écran.

Le code, pas à pas

1. Un lien, en tête du code source

<!-- Le tout premier élément du corps de la page -->
<a class="evitement" href="#contenu">Aller au contenu</a>

<header></header>
<nav></nav>
<main id="contenu" tabindex="-1"></main>

C’est un <a href>, pas un bouton : il mène à un endroit de la page, et le navigateur sait déjà tout faire. Sa place est avant ce qu’il permet d’éviter — placé après le menu, il ne fait plus rien gagner.

Le test 12.7.2 ajoute une exigence d’ensemble : dans chaque ensemble de pages — une rubrique, un blog, un espace client —, ce lien doit être au même endroit dans la présentation et dans le même ordre relatif dans le code. Il appartient au gabarit, pas à la page.

2. Le masquer sans le rendre inatteignable

.evitement {
	position: absolute;
	top: 0.5rem;
	left: 0.5rem;
	transform: translateY(-200%);
}

.evitement:focus {
	transform: translateY(0);
}

Le lien reste affiché et focalisable ; il est simplement déplacé hors du champ visible. La technique importe peu — transform, top: -100%, la classe sr-only complétée d’un :focus — pourvu qu’elle ne repose ni sur display: none, ni sur visibility: hidden, ni sur l’attribut hidden.

Le référentiel admet aussi un lien toujours visible : le test 12.7.2 demande qu’il soit « visible ou, à défaut, visible à la prise de focus ». Le masquer est une convention d’usage, pas une obligation. Un lien d’évitement permanent est plus simple à écrire, et plus difficile à casser.

Ne supprimez pas l’outline en stylant ce lien. Il apparaît au moment même où il prend le focus : c’est le seul repère qui dise où l’on se trouve.

3. tabindex="-1" sur la cible — le point qu’on rate

<main id="contenu" tabindex="-1"></main>

Une ancre déplace le défilement. Elle ne déplace le focus que si l’élément visé peut le recevoir — et <main>, <div>, <section>, <h1> ne le peuvent pas.

tabindex="-1" rend l’élément focalisable par script et par ancre, sans l’ajouter à l’ordre de tabulation. C’est la valeur exacte dont on a besoin ici : 0 l’ajouterait à l’ordre de tabulation, ce qu’on ne veut pas.

Le glossaire du référentiel est explicite sur ce qu’est un lien fonctionnel :

Un lien d’évitement ou d’accès rapide fonctionnel est un lien dont l’activation permet de reprendre la lecture et la navigation clavier à partir de la cible du lien lors de l’utilisation des navigateurs et des aides techniques retenus dans l’environnement de test […] de l’audit.

La vérification ne consiste pas à regarder où mène le Tab suivant : il continue dans le contenu dans les deux cas. Elle consiste à demander si quelque chose porte le focus après l’activation. Tab,

Entrée, puis un coup d'œil : la cible porte-t-elle un indicateur de focus ? Dans la console, `document.activeElement` désigne-t-il la cible, ou `body` ? Si c'est `body`, la cible ne prend pas le focus.

4. Plusieurs liens, quand la page les justifie

Un seul lien est exigé, vers la zone de contenu principal. Le critère 12.6 permet d’en ajouter d’autres — vers la recherche, vers la navigation — comme l’un des moyens de rendre ces zones atteignables ou évitables.

Ce n’est pas la seule façon de satisfaire 12.6 : un rôle de landmark correspondant à la nature de la zone suffit. Cinq liens d’évitement en tête de page coûtent cinq tabulations à qui n’en voulait qu’une ; deux ou trois au maximum — c’est un usage, pas une exigence du référentiel — et des landmarks pour le reste.

5. Dans une application à navigation par script

Un routeur qui intercepte le clic et rend la vue lui-même ne fait pas ce que fait le navigateur. Après un changement de page, le focus reste où il était, souvent sur le corps du document : le lien d’évitement en devient inopérant, et rien ne le signale.

La correction est la même partout : appeler cible.focus() explicitement après le rendu, sur un élément qui porte tabindex="-1".

6. Les landmarks ne dispensent pas du lien

<main>, <nav>, <header> permettent à un lecteur d’écran de sauter d’une zone à l’autre, et c’est précieux. Mais le critère 12.7 exige un lien, et pour une raison qui n’a rien de formel : une personne qui navigue au clavier sans lecteur d’écran n’a aucun accès aux landmarks. Pour elle, le menu se retabule en entier à chaque page tant que le lien n’existe pas.

Au clavier

ToucheEffet
Tab (au chargement)Amène le focus sur le lien d’évitement, qui devient visible. Il est le premier élément focalisable de la page.
EntréeActive le lien : la page défile jusqu’à la cible et le focus s’y déplace, si elle porte tabindex="-1". Sans lui, la page défile seule et document.activeElement retombe sur body.
Tab (après activation)Va au premier élément focalisable du contenu — dans les deux cas : le navigateur a posé là le point de départ de la tabulation. Cette touche ne dit donc pas si le focus a suivi ; l’indicateur de focus sur la cible, si.
Maj + TabRevient sur le lien d’évitement, qui réapparaît.

Ce que le lecteur d’écran restitue

Sur le lien : son intitulé et sa nature — « Aller au contenu, lien ». Après activation, c’est la cible qui est annoncée : sur un <main> porteur de tabindex="-1", de l’ordre de « Contenu principal, région principale », puis le début du contenu. La formulation varie d’un lecteur à l’autre ; les informations, non.

Si après activation vous n’entendez rien — ni la région, ni le début du contenu —, c’est que rien n’a reçu le focus : document.activeElement est retombé sur body, la cible n’est pas focalisable. Le silence est le symptôme, pas une nouvelle annonce du lien. Si vous n’entendez jamais le lien en tabulant depuis le haut de la page, il est masqué par display: none — ou bien quelque chose le précède dans l’ordre de tabulation.

Checklist

  • Un <a href="#…">, premier élément focalisable de la page.
  • Masqué par un déplacement hors champ, jamais par display: none ni hidden.
  • Visible à la prise de focus, avec son indicateur de focus intact.
  • La cible porte tabindex="-1"-1, pas 0.
  • Vérifier après activation que la cible porte bien le focus, et pas body.
  • Même place et même ordre relatif dans tout l’ensemble de pages.
  • Bonne pratique : deux ou trois liens au maximum, des landmarks pour les autres zones (le RGAA n’en fixe pas de plafond).

Les critères RGAA en jeu

  • Critère 12.7 — Lien d’évitement vers le contenu

    Le test 12.7.2 exige un lien fonctionnel et visible au moins à la prise de focus : caché en display none il ne prend jamais le focus, et pointé sur une cible qui ne le reçoit pas il déplace la vue sans poser le focus — rien n'est annoncé, rien ne montre où l'on est.

  • Critère 12.6 — Zones de regroupement atteignables

    Le lien d'évitement est l'un des cinq moyens admis pour qu'une zone soit atteinte ou évitée : c'est par lui que l'en-tête et le menu cessent de se retabuler en entier à chaque page.

  • Critère 12.8 — Cohérence de l’ordre de tabulation

    Sans focus posé sur la cible, la suite de la tabulation ne tient qu'au point de départ que le navigateur pose seul : le moindre script qui déplace le focus ailleurs le perd, et la tabulation repart du menu qu'on venait d'éviter.

  • Critère 10.7 — Visibilité de la prise de focus

    Le lien n'existe à l'écran qu'au moment où il prend le focus. Le styler en supprimant l'outline le rend invisible précisément quand il doit se voir.

Vérifiez votre composant avec l'extension

Pour aller plus loin

Sources