Respecter le contraste des couleurs WCAG sans deviner

Ce que mesure un rapport de contraste, les seuils AA et AAA pour le texte et les composants d'interface, et les erreurs de gris sur blanc qui échouent discrètement à tous les coups.

Pour un texte courant, le niveau AA des WCAG demande un rapport de contraste d'au moins 4,5:1 avec son arrière-plan. Un grand texte exige 3:1. Les composants d'interface et les graphiques porteurs de sens exigent 3:1. Le niveau AAA porte le texte à 7:1, et le grand texte à 4,5:1. Les rapports vont de 1:1 (couleurs identiques) à 21:1 (noir pur sur blanc pur).

Vous ne pouvez pas en juger à l'œil. Deux paires qui semblent également lisibles sur votre écran peuvent se situer de part et d'autre du seuil : le rapport doit donc être calculé.

Les seuils

De quoi il s'agitAAAAA
Texte courant4,5:17:1
Grand texte3:14,5:1
Composants d'interface et objets graphiques3:1pas d'exigence plus élevée

Ils découlent de trois critères de succès : 1.4.3 Contraste (minimum) au niveau AA, 1.4.6 Contraste (amélioré) au niveau AAA, et 1.4.11 Contraste du contenu non textuel au niveau AA, qui couvre les limites visuelles des contrôles et les parties d'un graphique nécessaires à sa compréhension.

Grand texte signifie au moins 18 points, ou 14 points en gras, ce qui revient à environ 24 px en normal ou 18,5 px en gras. Cela ne veut pas dire « un titre » : un titre de 20 px est du texte courant du point de vue des règles et réclame les 4,5:1 complets.

Quelques éléments sont exemptés : le texte purement décoratif, le texte d'un contrôle inactif, le texte faisant partie d'une image qui contient d'autres éléments visuels significatifs, et les logotypes. Les textes indicatifs et les textes d'aide ne le sont pas.

Ce que le rapport mesure réellement

La formule compare la luminance relative des deux couleurs :

ratio = (L_lighter + 0.05) / (L_darker + 0.05)

La luminance relative est un nombre allant de 0 (noir) à 1 (blanc), qui représente la quantité de lumière émise par une couleur. On la calcule en défaisant la courbe gamma sRGB sur chaque canal, puis en les pondérant :

L = 0.2126 R + 0.7152 G + 0.0722 B     (on linearised channels)

Le 0.05 ajouté des deux côtés modélise un peu de lumière ambiante réfléchie par l'écran, ce qui explique que le maximum soit 21 et non l'infini.

Deux conséquences en découlent. Le rapport ne dépend que de la luminance, pas de la teinte : deux couleurs peuvent donc avoir des teintes radicalement différentes et rester presque invisibles l'une sur l'autre. Et il est symétrique : peu importe laquelle des deux vous appelez le premier plan.

La luminance relative n'est pas la luminosité

Regardez ces coefficients. Le vert apporte environ 72 % de la luminance, le rouge environ 21 %, et le bleu à peine 7 %. C'est un modèle de la vision humaine, pas de l'intensité perçue d'une couleur, et il produit des résultats qui semblent faux tant qu'on ne s'y est pas habitué.

  • Le jaune pur #FFFF00 sur blanc donne environ 1,07:1, presque invisible, alors que le jaune passe pour une couleur forte.
  • Le même jaune sur noir donne environ 19,6:1, presque autant que du blanc sur noir.
  • Le bleu pur #0000FF sur noir donne environ 2,4:1 et échoue partout, bien qu'il paraisse franc et saturé.

C'est aussi pourquoi la luminosité HSL est un mauvais indicateur de substitution. Deux couleurs peuvent toutes deux se situer à 50 % de luminosité en HSL et présenter des luminances très différentes, parce que HSL traite la teinte et la luminosité comme indépendantes alors que la vision ne le fait pas. Convertissez, puis mesurez, au lieu de supposer qu'un pas de luminosité est un pas de contraste.

Les erreurs qui reviennent sans cesse

Du texte courant gris sur blanc. C'est la principale. Le gris le plus clair qui passe le niveau AA sur blanc est environ #767676, soit 4,54:1. Un cran plus clair, #777777, donne 4,48:1 et échoue. Les gris « atténués » très répandus comme #999999 tombent autour de 2,9:1, moins des deux tiers de l'exigence. Pour viser AAA sur blanc, il faut à peu près #595959, qui donne exactement 7:1.

Textes indicatifs et textes d'aide. On les règle plusieurs tons plus clairs que le corps de texte parce qu'ils sont secondaires. Ils restent du texte et exigent toujours 4,5:1.

Couleurs semi-transparentes. Un rapport calculé à partir de la couleur déclarée n'a aucun sens si l'élément s'affiche à 60 % d'opacité. Déterminez la couleur composée qui apparaît réellement à l'écran, puis mesurez celle-là.

Du texte sur des images, des dégradés et de la vidéo. Le rapport change d'un pixel à l'autre, et c'est le pire pixel qui compte. La solution habituelle est un voile derrière le texte, plutôt que d'espérer que la photo reste sombre.

Boutons aux couleurs de la marque. Du texte blanc sur une couleur de marque de valeur moyenne tombe très souvent entre 3:1 et 4,5:1 : il passe comme objet graphique et échoue comme libellé. Assombrissez le bouton, ou acceptez l'échec en connaissance de cause.

Indicateurs de focus et boutons réduits à une icône. Les deux relèvent de la règle des 3:1 pour le contenu non textuel. Un anneau de focus dans une nuance à peine différente de la même couleur est un oubli fréquent, tout comme une icône grise qui constitue l'unique contenu d'un bouton.

Le mode sombre par inversion. Retourner une palette claire ne préserve pas les rapports, car la luminance n'est pas symétrique autour du milieu. Du blanc pur sur du noir pur donne le maximum de 21:1, mais beaucoup de gens trouvent le halo inconfortable : un blanc cassé sur un gris très foncé, comme #E6E6E6 sur #121212 à environ 15:1, est un compromis courant.

Mesurer depuis une capture d'écran. Une pipette posée sur le bord d'un texte anticrénelé prélève un mélange de texte et d'arrière-plan, pas la couleur du texte. Prélevez au milieu d'un trait épais, ou mieux, lisez les valeurs dans votre CSS.

Une méthode de travail qui évite de deviner

Fixez d'abord l'arrière-plan, puis assombrissez ou éclaircissez le premier plan jusqu'à franchir le seuil avec un peu de marge, et notez cette paire. Construisez la palette à partir de paires approuvées plutôt que de couleurs approuvées : une couleur n'est pas accessible en soi, seulement en combinaison. Vérifiez aussi chaque état, pas seulement l'état au repos, et les deux thèmes si vous livrez un mode sombre.

Une dernière remarque. Un modèle perceptuel plus récent, APCA, est en cours d'élaboration pour une future version des WCAG et se comporte assez différemment, en particulier pour du texte clair sur fond sombre. Il vaut la peine d'être connu, mais les chiffres auxquels se conformer aujourd'hui sont les rapports WCAG 2 ci-dessus.