RGAA Lab

Composants accessibles

Le contraste et la couleur

Deux exigences qu'on confond : l'information ne doit pas tenir à la seule couleur, et le texte doit être assez contrasté. Les seuils du RGAA se comptent en pixels rendus, et le gras bascule à 18,5 px.

3 critères RGAA en jeu

Essayez-le

1. Ce que la couleur porte toute seule

Version fautive — vert pour conforme, rouge pour non conforme

  • Critère 1.1
  • Critère 3.2
  • Critère 8.3

Version corrigée — la couleur double un mot et un symbole

  • Critère 1.1  conforme
  • Critère 3.2  non conforme
  • Critère 8.3  conforme

En niveaux de gris, la colonne de gauche ne dit plus rien : son information tenait à la seule couleur. Le passage en gris n'est pas une simulation de daltonisme — c'est un contrôle grossier, et il suffit à révéler le défaut.

2. La même couleur, cinq verdicts

3 anomalies restent à traiter.

3 anomalies restent à traiter.

3 anomalies restent à traiter.

3 anomalies restent à traiter.

Cochez « Retirer les couleurs » : la liste de gauche perd son information, celle de droite la garde. Puis lancez la mesure — les cinq échantillons portent la même couleur, et n’obtiennent pas le même verdict, parce que le seuil, lui, dépend de ce qu’on mesure.

Cette démo existe aussi en page autonome.

L’erreur courante

Deux fautes, sur le même écran, et on ne les répare pas de la même façon.

/* À ne pas faire : le lien ne se distingue que par sa couleur */
a {
	color: #1b6ac9;
	text-decoration: none;
}

/* À ne pas faire : « c'est du gras, donc c'est du grand texte » */
.avertissement {
	font-size: 1rem;
	font-weight: 700;
	color: #8f8779;
}

Le lien sans soulignement au milieu d’un paragraphe est un lien pour qui distingue le bleu du noir, et un mot ordinaire pour tous les autres. Ce n’est pas une affaire de contraste : le rapport peut être excellent, l’information reste donnée par la seule couleur.

Le second cas est l’erreur de seuil. 1rem se rend à 16 px avec les réglages par défaut, donc en dessous des 18,5 px que le RGAA demande pour qu’un gras bénéficie du seuil allégé. Ce texte relève de 4,5:1, il plafonne à 3,5:1, et l’auteur croit être conforme parce qu’il l’a vu passer dans un vérificateur réglé sur « large text ».

Trois variantes, aussi répandues :

  • Le champ en erreur signalé par une bordure rouge seule. Qui ne voit pas le rouge ne voit pas d’erreur, et cherche pourquoi le formulaire refuse de partir.
  • La légende d’un graphique réduite à des pastilles de couleur. Les courbes deviennent interchangeables ; il faut une étiquette, un motif ou un repère.
  • Le texte d’indication d’un champ (placeholder) gris très clair. C’est du texte : il relève entièrement du critère 3.2, comme le reste de la page.

Le code, pas à pas

1. Deux exigences, deux réparations

Le critère 3.1 porte sur l’information : ce que la couleur dit, autre chose doit le dire aussi. Le critère 3.2 porte sur la lisibilité : ce qui est écrit doit se détacher de son fond. Un même élément peut satisfaire l’un et manquer l’autre, dans les deux sens.

a {
	color: var(--action); /* 3.2 : le rapport contre le fond */
	text-decoration: underline; /* 3.1 : l'indice qui ne dépend pas de la couleur */
	text-underline-offset: 0.2em;
}

Le référentiel n’impose pas le soulignement — il impose que l’information ne tienne pas à la seule couleur. Un poids typographique, une icône, un repère de forme, un mot ajouté font aussi bien, à condition d’être visibles. Le soulignement a seulement l’avantage d’être compris sans apprentissage.

2. Les seuils, et la mesure en pixels rendus

Le critère 3.2 pose quatre cas, et c’est la taille restituée qui les départage :

TexteSeuil
Sans effet de graisse, restitué à moins de 24 px4,5:1
En gras, restitué à moins de 18,5 px4,5:1
Sans effet de graisse, restitué à 24 px ou plus3:1
En gras, restitué à 18,5 px ou plus3:1

Le seuil du gras est à 18,5 px, et c’est là qu’on se trompe. Le WCAG énonce ces tailles en points — 18 pt, et 14 pt en gras ; le RGAA les convertit en pixels rendus. Lire ce « 14 » comme des pixels revient à accorder le seuil de 3:1 à des textes qui relèvent de 4,5:1, c’est-à-dire à presque tous les gras d’une interface.

Le critère s’applique aussi au texte en image, aux sous-titres et au texte incrusté dans une vidéo. Il ne s’applique pas au texte d’un logo ou d’une marque, au texte purement décoratif, ni à un élément d’interface inactif — un bouton disabled, par exemple.

3. Mesurer ce qui est rendu, pas ce qui est déclaré

const style = getComputedStyle(element);
style.fontSize; // « 16px », quelle que soit l'unité déclarée
style.fontWeight; // « 700 »
style.color; // « rgb(143, 135, 121) »

getComputedStyle résout les em, les rem, les pourcentages et les héritages : c’est la seule valeur qui corresponde à ce que le référentiel appelle la taille restituée. Un font-size: 0.875em dans un conteneur déjà réduit descend plus bas qu’on ne l’avait prévu, et bascule sous un seuil sans que la feuille de styles le montre.

Deux situations résistent à toute mesure automatique, la nôtre comprise : un fond semi-transparent, et un texte posé sur une image ou un dégradé. Il faut alors reprendre le calcul à la main, sur la zone la moins favorable — c’est ce que dit le glossaire pour la couleur contiguë d’un dégradé.

4. Les composants d’interface : 3:1, dans chaque état

Le critère 3.3 est celui qu’on oublie. Il ne parle pas de texte mais des couleurs d’un composant d’interface et des éléments graphiques porteurs d’information, comparées à la couleur d’arrière-plan contiguë — celle qui touche le bord extérieur du composant.

.champ {
	border: 1.5px solid var(--bordure); /* 3:1 contre le fond de la page */
}

.champ:focus-visible {
	outline: 2px solid var(--focus); /* un état, donc à mesurer aussi */
	outline-offset: 2px;
}

Ce qui relève de ce critère : la bordure qui rend un champ repérable, la coche d’une case, la position d’un interrupteur, l’indicateur de focus quand vous l’avez redessiné, les segments d’un graphique dont la couleur est nécessaire à la compréhension.

Ce qui n’en relève pas, le référentiel le dit expressément : un composant inactif, un composant dont l’apparence reste celle du navigateur sans aucune retouche, et un composant que sa position, sa taille ou son texte suffisent à identifier sans la couleur.

5. Décidez-le dans la palette, pas dans les composants

Le contraste ne se rattrape pas composant par composant. Une couleur qui échoue échoue partout où elle sert, et corriger trois cents écrans un par un n’aboutit jamais. Fixez les couples texte/fond une fois, au niveau des jetons, et vérifiez les couples — pas les couleurs isolées.

Les tests admettent une porte de sortie : un mécanisme qui permet d’afficher les contenus avec un rapport conforme. Elle est plus étroite qu’elle n’en a l’air. Le glossaire exige que ce mécanisme conserve à l’identique les contenus et les fonctionnalités, et qu’il soit lui-même conforme aux critères 3.2 et 3.3 sans recourir à un autre mécanisme. Un thème « contraste élevé » à moitié tenu ne sauve rien.

Au clavier

Le contraste ne se navigue pas : ces touches sont celles de la démo. Le clavier croise pourtant le sujet en un point, et c’est le plus souvent manqué — le style de focus.

ToucheEffet
TabAmène le focus sur la case à cocher, puis sur le champ, puis sur le bouton de mesure.
EspaceCoche ou décoche « Retirer les couleurs ».
Entrée / EspaceSur le bouton, relance la mesure des styles rendus.

Tabulez sur le champ de la démo. Le cas particulier du critère 3.3 vise un composant dont l’apparence est gérée par les styles natifs du navigateur sans aucune modification par l’auteur ; ce champ-là est restylé — bordure, rayon, fond, espacement — et seule son indication de focus reste native. La condition n’est donc remplie que pour ce contour, et dès que vous le redessinez, il devient un état à mesurer, à 3:1 au moins. Sa bordure, elle, se mesure dans tous les cas : c’est ce que fait la ligne « Champ de saisie » du tableau.

Ce que le lecteur d’écran restitue

Rien. Aucun lecteur d’écran n’annonce un rapport de contraste, et aucun ne restitue la couleur dans la lecture courante — NVDA et JAWS savent la dire sur commande, personne ne l’entend en lisant. C’est précisément l’argument du critère 3.1 : une information qui n’existe qu’en couleur n’existe pas du tout pour qui écoute la page. La liste fautive de la démo se lit « Critère 1.1, Critère 3.2, Critère 8.3 » — trois intitulés, aucun état. La liste corrigée se lit « Critère 3.2, non conforme ».

La formulation varie d’un lecteur à l’autre, mais la lecture courante laisse le même trou. Si vous écoutez une page et que l’état, la sélection, l’erreur ou la destination ne s’entendent nulle part alors qu’ils se voient à l’écran, l’information était dans la couleur. C’est le contrôle le plus rapide qui soit pour le critère 3.1 — et il ne dit rien du critère 3.2, qui ne se vérifie qu’à la mesure.

Checklist

  • Doubler toute information portée par la couleur : un mot, une forme, un symbole.
  • Vérifier les liens en contexte de texte : couleur seule, jamais.
  • Mesurer sur la taille restituée, pas sur l’unité déclarée.
  • Retenir 18,5 px pour le gras et 24 px pour le reste, avant d’appliquer 3:1.
  • Mesurer les bordures, coches et indicateurs de focus redessinés à 3:1.
  • Reprendre à la main tout texte posé sur une image, un dégradé ou un fond translucide.
  • Fixer les couples de couleurs dans les jetons, pas dans chaque composant.

Les critères RGAA en jeu

Vérifiez votre composant avec l'extension

Pour aller plus loin

Sources