contrastRatio

expérimentaldepuis 0.1.0@legba-core/ui

contrastRatio

Ce que ça fait

Calcule le rapport de contraste WCAG 2.x entre deux couleurs, dans [1, 21].

Signature

function contrastRatio(foreground: string | Rgb | Rgba, background: string | Rgb | Rgba): number

Paramètres

NomTypeRequisDéfautDescription
foregroundstring | Rgb | RgbaouiCouleur de premier plan (texte, icône, anneau de focus). Peut être translucide.
backgroundstring | Rgb | RgbaouiCouleur de fond. Doit être opaque.

Formes de chaîne reconnues : #rgb, #rrggbb, rgb(r, g, b), rgba(r, g, b, a), rgb(r g b), rgb(r g b / a), transparent. Les canaux et l’alpha acceptent la notation en pourcentage. Les couleurs nommées (red) et les autres espaces colorimétriques (color(display-p3 …)) ne sont pas reconnus.

Retour

Le rapport de contraste, 1 pour deux couleurs identiques, 21 pour noir sur blanc. Un foreground translucide est d’abord composé sur background, puis comparé.

Erreurs

CodeQuandComment corriger
TypeErrorUne couleur n’est pas analysablePasser une des formes listées ci-dessus, ou un objet Rgb
TypeErrorbackground a un alpha inférieur à 1Composer d’abord ce fond sur la couleur qu’il recouvre, puis appeler

Exemple minimal

import { contrastRatio } from "@legba-core/ui";

const ratio = contrastRatio("#8a4e12", "#f4f2ec");

Exemple complet

import { contrastRatio, meetsContrastAA } from "@legba-core/ui";

/**
 * Vérifie qu'une redéfinition de jeton reste conforme AA avant de la
 * poser — à appeler depuis les tests de l'application intégratrice.
 */
function assertAccentLisible(accent: string, fond: string): void {
  const ratio = contrastRatio(accent, fond);
  if (!meetsContrastAA(ratio)) {
    throw new Error(`${accent} sur ${fond} ne donne que ${ratio.toFixed(2)}:1, il en faut 4,5.`);
  }
}

assertAccentLisible("#8a4e12", "#f4f2ec");

Pièges

  • Le seuil n’est PAS dans cette fonction : elle rend un nombre, la décision appartient à meetsContrastAA. Comparer soi-même à 4.5 fonctionne, mais rate le cas du texte large (seuil 3).
  • background doit être opaque, et la fonction lève plutôt que de supposer du blanc derrière un fond translucide. Une valeur lue par getComputedStyle(el).backgroundColor vaut très souvent rgba(0, 0, 0, 0) sur un élément sans fond propre : il faut remonter les ancêtres (frontières de Shadow DOM comprises) jusqu’au premier fond opaque avant d’appeler.
  • L’opacity d’un élément n’apparaît PAS dans sa color calculée. Pour un texte atténué par opacity, composer soi-même la valeur (rgb(r g b / opacity)) avant de comparer, sinon le rapport mesuré est meilleur que le rapport réel.
  • Le calcul suit WCAG 2.x (luminance relative sRGB). Il ne met pas en œuvre APCA / WCAG 3, qui donne des valeurs incomparables.
  • Un rapport conforme ne garantit pas une interface accessible : la taille de cible, le focus visible et les noms accessibles sont des critères distincts.

Voir aussi