RGAA Lab

Composants accessibles

Le gabarit d'une page

Le titre de page est la première chose annoncée à l'arrivée, et le seul libellé de l'onglet quand on en a quinze d'ouverts. Avec le type de document et la validité du code, c'est ce qui porte la page entière sans être un composant. Démo, code, critères RGAA.

5 critères RGAA en jeu

Essayez-le

Convention de titre
  • Rectorat de l'académie de Bordeaux — Demande de bourse — Étape 2 sur 4 : vos coordonnées
  • Rectorat de l'académie de Bordeaux — Demande de bourse — Étape 3 sur 4 : vos ressources
  • Rectorat de l'académie de Bordeaux — Recherche — Résultats pour « internat »
  • Rectorat de l'académie de Bordeaux — Erreur — Page introuvable
  • Rectorat de l'académie de Bordeaux — Espace personnel — Suivre mes demandes

À l'arrivée sur la première page, un lecteur d'écran annonce : Rectorat de l'académie de Bordeaux — Demande de bourse — Étape 2 sur 4 : vos coordonnées

Et le document que vous lisez : son titre est , et son analyseur travaille en mode de compatibilité — document.compatMode vaut BackCompat, signe qu'aucune déclaration de type de document valide ne précède l'élément racine.

Cinq pages d’un même site, dans une barre d’onglets à la largeur d’une barre d’onglets bien remplie. Changez de convention aux flèches, sans quitter le groupe de boutons radio, et regardez le compte : ce ne sont pas cinq onglets que le nom du site en tête laisse discernables. La coupure est celle du navigateur, et ce qui reste lisible est mesuré avec la police réellement appliquée — pas estimé en nombre de caractères.

Cette démo existe aussi en page autonome.

L’erreur courante

Le titre qui commence par le nom du site.

<!-- À ne pas faire : cinq mots identiques avant l'information utile -->
<title>
	Rectorat de l'académie de Bordeaux — Demande de bourse — Étape 2 sur 4
</title>

Ce titre est exact, complet, et il ne sert à rien de ce pour quoi un titre existe.

Dans l’onglet, il ne reste que « Rectorat de l’aca… » — et comme les quinze onglets ouverts du même site affichent exactement la même chose, retrouver l’étape 2 demande de les survoler un par un. Dans l’historique et les favoris, tout se trie sous la même entrée. Et au lecteur d’écran, ces cinq mots sont annoncés en premier à chaque chargement de page : l’information qu’on attendait — où suis-je arrivé ? — vient après.

Trois variantes coûtent la même chose :

  • Le titre laissé par le générateur : « Document », « Untitled », « React App ». La balise existe, le critère 8.5 est satisfait ; c’est le 8.6 qui échoue, et l’échec est total puisque le titre ne décrit rien.
  • Le titre vide. Même situation, et c’est le seul cas que notre outil tranche seul : un titre vide ne décrit aucune page, il n’y a rien à relire.
  • Le titre figé d’une application à navigation côté client. Rendu une fois au premier chargement, il reste celui de la première vue pour toute la session — quinze pages, un titre.

Le code, pas à pas

1. La déclaration de type de document, avant tout le reste

<!-- La première ligne du fichier servi, avant l'élément racine -->
<!doctype html>

Le critère 8.1 se lit en trois tests, et le troisième est celui qu’on oublie : le doctype doit être présent, valide, et situé avant la balise html dans le code source. Une déclaration repoussée plus bas par un gabarit qui écrit quelque chose avant elle n’est pas « mal placée » : elle est purement ignorée par l’analyseur du navigateur, et la page se comporte comme si elle n’existait pas.

Ce qu’on y perd n’est pas cosmétique. Sans type de document, le navigateur bascule en mode de compatibilité, où les règles de calcul du rendu ne sont plus celles d’aujourd’hui — et l’arbre d’accessibilité construit par-dessus ne l’est plus non plus. Le contrôle tient en une ligne de console :

// « CSS1Compat » en mode standard, « BackCompat » en mode de compatibilité.
document.compatMode;

Deux pièges, tous deux hérités de gabarits jamais repris. Un doctype de l’époque XHTML, avec sa partie PUBLIC, n’est pas invalide en soi mais fait cohabiter deux jeux de règles avec du balisage moderne. Et une page servie dans un cadre est un document à part entière : elle a sa propre déclaration, à vérifier séparément.

Le référentiel définit le type de document comme l’« ensemble de données de référence qui permet aux agents utilisateurs de connaître les caractéristiques techniques des langages utilisés sur la page web » (« type de document » au glossaire).

2. Un titre, sur chaque page

Le test 8.5.1 ne demande qu’une chose : « Chaque page web a-t-elle un titre de page (balise <title>) ? ». C’est le critère le plus simple du référentiel, et il échoue quand même — pas sur la page d’accueil, mais sur les gabarits qu’on ne regarde jamais : la page d’erreur 404, la version imprimable, le document généré à la volée, la vue rendue par script après une navigation.

La raison est toujours la même : le titre ne se voit pas dans la page. On peut travailler une journée sur un gabarit sans jamais lever les yeux vers l’onglet.

3. Le titre commence par ce qui distingue la page

C’est le point qui compte, et le référentiel le fixe par sa définition :

Contenu de la balise <title> d’une page web permettant d’identifier de manière claire, concise et unique les contenus/la nature de la page (« Plan du site www.nomdusite.fr » pour une page présentant le plan du site web, par exemple).

Trois exigences dans une phrase, et « unique » est la plus tranchante : deux pages différentes ne peuvent pas porter le même titre. Un parcours de quatre étapes qui s’intitule quatre fois « Demande de bourse » y échoue, même si le titre est excellent par ailleurs.

<!-- Ce qui distingue la page d'abord, le nom du site en queue -->
<title>Étape 2 sur 4 : vos coordonnées — Demande de bourse — Académie de Bordeaux</title>

Le référentiel n’impose aucun ordre : il demande un titre clair, concis et unique. Mais l’ordre découle des trois mots plus qu’il ne s’y ajoute — un titre n’est ni concis ni identifiant si ses dix premiers mots sont les mêmes partout. C’est aussi ce que fait la démo ci-dessus : la convention n’est qu’une permutation, et le nombre d’onglets discernables passe de un à cinq.

4. Le titre porte l’état de la page

Une page n’est pas toujours une page fixe. Quand son contenu dépend d’une action, le titre doit suivre :

<title>Résultats pour « internat » : 12 réponses — Académie de Bordeaux</title>
<title>Erreur — Étape 2 sur 4 : vos coordonnées — Demande de bourse</title>
<title>Page introuvable — Académie de Bordeaux</title>

Le préfixe « Erreur » d’un formulaire renvoyé en échec est ce qui permet de savoir, avant même de lire la page, que la soumission n’est pas passée — il va de pair avec le récapitulatif d’erreurs.

5. Le titre suit la navigation côté client

// Une navigation côté client ne repasse pas par le serveur :
// le titre reste celui de la première vue tant que rien ne le change.
document.title = `${titreDeLaVue} — Académie de Bordeaux`;

Ce changement n’est pas annoncé de lui-même : il vaut pour l’onglet, l’historique et les favoris, et pour le lecteur d’écran à la prochaine occasion d’annoncer le titre. Beaucoup d’applications déplacent en plus le focus vers le titre principal de la nouvelle vue, ce qui donne l’annonce que la navigation n’a pas produite — c’est une bonne pratique reconnue, pas une exigence du référentiel.

Un point de méthode qui va avec : un audit ne peut pas vérifier ce critère sur le code servi. Il faut naviguer, vue par vue, et relire l’onglet à chaque fois.

6. La validité : cinq points, et rien d’autre

Le test 8.2.1 est limitatif. Le code source généré doit vérifier ces conditions, et le validateur du W3C dit beaucoup d’autres choses qui ne comptent pas ici :

Les balises, attributs et valeurs d’attributs respectent les règles d’écriture ; L’imbrication des balises est conforme ; L’ouverture et la fermeture des balises sont conformes ; Les valeurs d’attribut id sont uniques dans la page ; Les attributs ne sont pas doublés sur un même élément.

Ce n’est pas du purisme, et les deux exemples suivants disent pourquoi.

Un id en double est le défaut le plus fréquent de la liste, et le plus coûteux. <label for> et aria-labelledby ne retiennent que la première occurrence : le second champ récupère le nom du premier, ou n’en a plus du tout. La cause est presque toujours la même — un composant réutilisé plusieurs fois dans la page, qui emporte ses id avec lui.

<!-- À ne pas faire : le composant est inclus deux fois, l'id aussi -->
<label for="courriel">Adresse électronique</label>
<input id="courriel" type="email" /><label for="courriel">Adresse électronique du tuteur</label>
<input id="courriel" type="email" />

Une balise mal fermée déplace tout ce qui suit. L’analyseur répare comme il peut, et sa réparation déplace des blocs entiers dans l’arbre : un <main> qui se retrouve dans le <nav>, une liste avalée par la cellule précédente. Le contenu s’affiche à peu près, et la navigation par régions annonce n’importe quoi.

D’où la seule méthode qui marche : le code source généré, pas l’inspecteur. Le navigateur a déjà réparé le DOM que l’inspecteur affiche ; les balises mal fermées et les imbrications fautives y ont disparu. Sur une application rendue côté client, c’est le DOM sérialisé qu’il faut coller au validateur, puisque le source initial ne contient presque rien.

7. Les changements du sens de lecture

<p>
	Le rapport reprend le titre
	<span lang="ar" dir="rtl">الوصول الشامل</span>, publié en 2019.
</p>

Le critère 8.10 fonctionne exactement comme les changements de langue, avec un autre attribut. Chaque texte dont le sens de lecture diffère de celui de la page est contenu dans un élément portant un attribut dir (test 8.10.1), et la valeur de cet attribut doit être « conforme (rtl ou ltr) » et « pertinente » (test 8.10.2).

Deux conséquences qui découlent du texte des tests :

  • direction: rtl en CSS ne remplace pas l’attribut. Le test demande l’attribut, et rien d’autre ne le remplace ; la raison, que le référentiel ne formule pas, est que le sens de lecture est porté par le document.
  • dir="auto" n’est pas une des deux valeurs attendues, même s’il donne souvent le bon résultat à l’écran.

Le glossaire ajoute la règle par défaut : « en l’absence d’indication de sens de lecture via l’attribut dir sur l’élément html, body, ou un des parents du texte analysé, le sens de lecture par défaut est de gauche à droite (valeur ltr) » (« sens de lecture » au glossaire). Une page française n’a donc rien à déclarer tant qu’elle ne cite personne.

Le cas où l’erreur se voit à l’œil nu est le texte mixte : un sigle latin, un nombre ou une parenthèse dans une phrase arabe sortent à la mauvaise place, la ponctuation en tête de ligne.

Au clavier

Rien de tout cela n’est interactif : ni le type de document, ni le titre, ni le sens de lecture n’entrent dans un ordre de tabulation. La vérification se fait au navigateur et au lecteur d’écran.

ToucheEffet
TabNe s’arrête sur rien ici. Le titre de page n’est pas un élément de la page : il est dans son en-tête.
Ctrl + U ( + + U)Affiche le code source envoyé par le serveur — le seul endroit où la déclaration de type de document se lit. L’inspecteur, lui, montre un DOM déjà réparé.
Insert + TNVDA et JAWS : relisent le titre de la page, sans recharger. Le contrôle le plus rapide après une navigation côté client.
Ctrl + + F2VoiceOver : annonce le nom de la fenêtre, c’est-à-dire le titre de page.

Ce que le lecteur d’écran restitue

À l’arrivée sur la page, le titre vient avant tout le reste — « Étape 2 sur 4 : vos coordonnées, Demande de bourse, Académie de Bordeaux », suivi du nom du navigateur selon les configurations. C’est la seule information dont on dispose avant d’avoir commencé à lire, et elle est répétée à chaque changement de page. La formulation varie d’un lecteur à l’autre ; ce qui est annoncé, non.

Si vous entendez l’adresse de la page ou un nom de fichier à la place, il n’y a pas de titre : c’est le 8.5. Si vous entendez la même longue phrase au début de chaque page d’un parcours, le nom du site est en tête, ou le titre ne change pas — c’est le 8.6, et la démo ci-dessus dit lequel des deux. Et si l’annonce ne bouge pas alors que la vue a changé, document.title n’est pas mis à jour par le routeur.

Checklist

  • Une déclaration doctype présente, valide, et avant l’élément racine — sur chaque gabarit, pas seulement l’accueil.
  • document.compatMode vaut CSS1Compat : sinon la page est en mode de compatibilité.
  • Un titre sur chaque page, page d’erreur et vues rendues par script comprises.
  • Le titre commence par ce qui distingue la page ; deux pages n’ont jamais le même.
  • Une navigation côté client met à jour document.title à chaque vue.
  • Au validateur, ne retenir que les cinq points du test 8.2.1 — les id en double d’abord.
  • Chaque passage de sens contraire dans un élément portant dir="rtl" ou dir="ltr", jamais du CSS seul.

Les critères RGAA en jeu

  • Critère 8.1 — Type de document

    Sans déclaration de type de document, le navigateur bascule en mode de compatibilité : le rendu et l'arbre d'accessibilité ne sont plus ceux qu'attendent les technologies d'assistance, et une déclaration placée après l'élément racine est purement ignorée.

  • Critère 8.2 — Validité du code source

    Le test énumère cinq points, et l'un d'eux casse tout le reste : un attribut id en double, et aria-labelledby comme label for ne retiennent que la première occurrence — le champ récupère le nom d'un autre.

  • Critère 8.5 — Titre de page

    Le titre ne se voit nulle part dans la page : il se voit dans l'onglet. C'est pourquoi ce sont les gabarits hors du chemin principal — pages d'erreur, vues rendues par script — qui le perdent sans que personne ne le remarque.

  • Critère 8.6 — Pertinence du titre de page

    Le nom du site en tête de chaque titre repousse l'information utile derrière une dizaine de mots identiques, annoncés à chaque page et coupés dans chaque onglet : c'est ici que le critère se joue, pas sur la présence.

  • Critère 8.10 — Changements du sens de lecture

    Le test demande un attribut dir sur l'élément qui contient le passage arabe ou hébreu, et rien d'autre : une page qui s'en remet à direction: rtl en CSS échoue, même quand le rendu est juste à l'écran.

Vérifiez votre composant avec l'extension

Pour aller plus loin

Sources