Développement web

Contraste des couleurs WCAG : calcul, ratios exigés et conformité sans sacrifier le design

Ce qu’est le ratio de contraste, comment il se calcule à partir de la luminance, ce qu’exigent les niveaux AA et AAA, comment le mesurer et les erreurs fréquentes.

Le contraste des couleurs est la différence de luminosité entre un texte (ou un élément graphique) et son arrière-plan, exprimée par un ratio de 1:1 (aucun contraste) à 21:1 (noir sur blanc). Les WCAG, la norme d’accessibilité du web, exigent au moins 4,5:1 pour le texte normal et 3:1 pour le texte de grande taille et les composants d’interface au niveau AA, et 7:1 au niveau AAA. Ce n’est pas une préférence esthétique : un gris clair sur blanc qui vous paraît élégant est illisible pour une part réelle des visiteurs, et Lighthouse, axe et tout audit le signalent.

Cet article explique comment se calcule le ratio, ce qu’exige chaque critère, comment le mesurer et comment corriger les cas typiques sans refaire la palette.

Comment se calcule le ratio

La formule est dans la définition des WCAG :

ratio = (L1 + 0,05) / (L2 + 0,05)

où L1 est la luminance relative de la couleur la plus claire et L2 celle de la plus sombre, toutes deux entre 0 (noir) et 1 (blanc). La luminance se déduit des composantes sRGB : chaque canal est normalisé entre 0 et 1, linéarisé (s’il est ≤ 0,04045 on le divise par 12,92 ; sinon ((c + 0,055) / 1,055) ^ 2,4) et les canaux sont combinés avec les poids 0,2126 R + 0,7152 G + 0,0722 B. Le vert pèse bien plus que le bleu parce que l’œil humain y est plus sensible : c’est pourquoi un bleu saturé sur noir contraste moins qu’il n’y paraît.

En JavaScript :

function luminance([r, g, b]) {
  const lin = (c) => {
    c /= 255;
    return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
  };
  return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
}
function contrast(a, b) {
  const [l1, l2] = [luminance(a), luminance(b)].sort((x, y) => y - x);
  return (l1 + 0.05) / (l2 + 0.05);
}
contrast([29, 29, 31], [255, 255, 255]); // #1d1d1f sur blanc ≈ 16,8:1
contrast([110, 110, 115], [255, 255, 255]); // #6e6e73 sur blanc ≈ 5,1:1
contrast([134, 134, 139], [255, 255, 255]); // #86868b sur blanc ≈ 3,6:1 (insuffisant pour du texte normal)

Les trois exemples sont les gris de ce blog : le premier est le texte principal, le deuxième le gris que nous utilisons pour le texte secondaire (il passe AA) et le troisième le gris d’Apple avec lequel nous avions commencé et qu’il a fallu abandonner pour le petit texte.

Ce qu’exige chaque critère

Critère Niveau S’applique à Ratio minimal
1.4.3 Contraste (minimum) AA Texte normal 4,5:1
1.4.3 Contraste (minimum) AA Texte de grande taille (≥ 24 px, ou ≥ 18,66 px en gras) 3:1
1.4.6 Contraste (amélioré) AAA Texte normal 7:1
1.4.6 Contraste (amélioré) AAA Texte de grande taille 4,5:1
1.4.11 Contraste non textuel AA Composants d’interface (bordures de champs, icônes nécessaires, indicateurs de focus) et graphiques informatifs 3:1

Exemptés : le texte décoratif, les logos, le texte des contrôles désactivés et le texte faisant partie d’une image purement décorative. Un placeholder de champ de formulaire n’est pas exempté : c’est un texte que l’utilisateur doit lire.

La « grande taille » est définie en points (18 pt, ou 14 pt en gras) ; en CSS, cela correspond à environ 24 px et 18,66 px.

Comment le mesurer

  • Outil de couleurs d’AIMRAN Tools : saisissez la couleur du texte et celle du fond (en HEX, RGB, HSL ou OKLCH) ; il affiche le ratio et indique s’il passe AA et AAA pour le texte normal et de grande taille. Il fonctionne dans le navigateur.
  • Chrome DevTools : en inspectant un texte, le sélecteur de couleur du panneau Styles affiche le ratio et deux lignes sur la roue chromatique marquant où commencent AA et AAA. Le panneau CSS Overview liste toutes les paires à contraste insuffisant de la page.
  • Lighthouse / axe : l’audit color-contrast énumère les éléments en échec avec leur ratio actuel.
  • Firefox : l’inspecteur d’accessibilité propose une vérification de contraste avec simulation du daltonisme.

Les outils automatiques ne détectent pas le texte sur image ou sur dégradé : là, il faut mesurer contre la zone la plus claire (ou la plus sombre) du fond susceptible de se trouver sous le texte.

Les défauts les plus fréquents et comment les corriger

  1. Un gris « subtil » pour le texte secondaire. #999 sur blanc donne 2,9:1. Descendez à #6e6e73 (5,1:1) ou #595959 (7:1). La hiérarchie s’obtient aussi par la taille et la graisse, pas seulement par la couleur.
  2. Du texte blanc sur des couleurs de marque claires. Blanc sur #0071e3 donne 4,7:1 (ça passe) ; sur #34c759 (vert), seulement 2,2:1. Pour les étiquettes de couleurs claires, utilisez un texte sombre ou mélangez la couleur avec du noir : sur ce blog, les étiquettes de catégorie utilisent color-mix(in srgb, var(--c) 62%, black) pour le texte et la couleur pure uniquement pour les icônes et les fonds.
  3. Des liens qui ne se distinguent que par la couleur. Outre le contraste avec le fond, un lien dans un paragraphe doit se différencier du texte normal (les WCAG demandent 3:1 entre les deux ou un signal supplémentaire, comme le soulignement). Souligner est le plus simple.
  4. Des placeholders clairs. Le gris par défaut de nombreux navigateurs n’atteint pas 4,5:1. Définissez ::placeholder { color: #6e6e73 }.
  5. Un indicateur de focus invisible. Le outline du clavier doit avoir 3:1 contre le fond adjacent. Un outline: 2px solid de la couleur primaire sur fond blanc passe en général ; sur un bouton de la même couleur, non : ajoutez un outline-offset pour que le contour repose sur le fond.
  6. Le mode sombre non vérifié. Les gris qui fonctionnent sur blanc ne fonctionnent pas sur #000. Mesurez les deux palettes séparément.

Une note sur APCA

Les WCAG 3, encore à l’état de brouillon, étudient un algorithme différent (APCA) qui tient mieux compte de la polarité (texte clair sur sombre ou sombre sur clair) et de la taille de police. C’est une référence de design intéressante, mais l’exigence légale et celle que mesurent les audits aujourd’hui est le ratio des WCAG 2 : c’est donc celui qu’il faut respecter.

Conclusion

Le contraste se calcule avec une formule fixe à partir de la luminance des deux couleurs et se compare à trois seuils : 4,5:1 pour le texte, 3:1 pour le texte de grande taille et les composants, 7:1 si vous visez AAA. Mesurez avec DevTools ou un outil de contraste, vérifiez le clair et le sombre séparément et rappelez-vous que la hiérarchie visuelle ne dépend pas que du gris : taille, graisse et espacement font le même travail sans exclure personne.

Sources et références

  1. WCAG 2.2 : Critère 1.4.3 Contraste (minimum) w3.org
  2. WCAG 2.2 : Critère 1.4.6 Contraste (amélioré) w3.org
  3. WCAG 2.2 : Critère 1.4.11 Contraste du contenu non textuel w3.org
  4. WCAG 2.2 : définition du ratio de contraste et de la luminance relative w3.org
  5. Chrome Lighthouse : Background and foreground colors do not have a sufficient contrast ratio developer.chrome.com

Outils associés

Herramientas gratuitas de AIMRAN Tools que funcionan en tu navegador, sin registro.

Ver todas las herramientas