HEX, RGB, HSL et les espaces colorimétriques plus récents
Une couleur en CSS, ce sont trois ou quatre nombres accompagnés d'une règle de lecture : l'espace colorimétrique. Les palettes qui paraissent irrégulières à l'écran sont en général échelonnées dans un espace qui ne correspond pas au fonctionnement de la vision.
HEX et RGB sont les mêmes nombres
#3B82F6, ce sont trois octets en base 16 : 3B vaut 59, 82 vaut 130, F6 vaut 246, si bien que rgb(59, 130, 246) est la même couleur en base 10, et un convertisseur de couleurs passe de l'un à l'autre sans perte.
La notation abrégée à trois chiffres duplique chaque chiffre au lieu de le compléter par des zéros : #F80 s'étend donc en #FF8800, pas en #F08000 ; seules 4 096 couleurs ont une forme courte. #RGBA ajoute un chiffre d'alpha de la même manière et #RRGGBBAA détaille les quatre octets, cet octet d'alpha allant de 0 à 255 (1A vaut 10 %, 80 vaut 50 %, CC vaut 80 %) alors que tous les autres alphas en CSS vont de 0 à 1. La syntaxe moderne accepte aussi des composantes séparées par des espaces, l'alpha venant après une barre oblique : rgb(59 130 246 / 50%).
Ces nombres ne sont pas des quantités de lumière. sRGB encode chaque canal à travers une courbe de transfert d'environ 2,2 : 128 émet donc environ 21,6 % de la lumière qu'émet 255, et non la moitié, et tout mélange calculé sur les valeurs brutes hérite de cette courbe.
HSL, HSV et la luminosité qui n'en est pas une
HSL réorganise le même cube RGB en cylindre : la teinte est un angle (0 rouge, 120 vert, 240 bleu), la saturation est la distance à l'axe gris central, et la luminosité va de 0 à 50 %, la teinte pure, puis jusqu'à 100 %, le blanc. HSV remplace la luminosité par la valeur, dont le sommet est la teinte pure plutôt que le blanc, si bien que les teintes claires s'obtiennent en baissant la saturation. Ni l'un ni l'autre ne sait quoi que ce soit de la perception.
| Couleur | HSL | Luminance relative | Luminosité OKLCH |
|---|---|---|---|
#FFFF00 | hsl(60 100% 50%) | 0,928 | 0,97 |
#0000FF | hsl(240 100% 50%) | 0,072 | 0,45 |
Les deux revendiquent 50 % de luminosité ; l'une émet environ treize fois plus de lumière que l'autre. Une rangée de jetons dont chaque teinte est fixée à 50% se lit comme un fouillis de poids visuels.
Échelonner la luminosité HSL à teinte et saturation constantes échoue pour la même raison, plus une autre : la saturation se mesure par rapport à la place disponible à cette luminosité, si bien que le palier à 50 % est le plus coloré et que tout ce qui l'entoure se délave. L'angle de teinte se maintient également pendant que le chroma perçu s'effondre, et c'est ce qui rend les paliers intermédiaires ternes. Ce ne sont pas des versions assombries de la couleur de base, ce sont des versions grisées d'une quantité que personne n'a choisie.
Le jaune en donne le pire exemple. Baisser la luminosité HSL fait glisser un jaune droit vers l'olive, parce qu'un jaune foncé à cet angle de teinte est tout simplement de l'olive, et le garder jaune suppose de faire tourner la teinte vers l'orange en descendant. Le bleu connaît l'ennui inverse : il se situe si bas en luminance que toute sa variation utilisable vit au-dessus de 50 %.
LCH et OKLCH
LCH est la forme polaire du CIE Lab, bâti à partir d'expériences d'appariement de couleurs : luminosité de 0 à 100, chroma (l'intensité colorée, sans maximum fixe) et un angle de teinte. OKLCH est la même construction au-dessus d'Oklab, qui corrige les décalages de teinte pour lesquels Lab est connu, le plus visible étant les bleus qui virent au violet en s'éclaircissant.
.a { color: oklch(0.62 0.21 260); } /* L 0 to 1, C about 0 to 0.37, H in degrees */
.b { color: oklch(62% 0.21 260 / 50%); } /* percentages and alpha both allowed */
.c { color: lch(52% 82 285); } /* L runs 0 to 100 here */
L'uniformité perceptuelle signifie qu'une distance numérique égale correspond de près à une distance perçue égale. Une rampe par pas de 0,08 de luminosité donne l'impression de paliers réguliers, une rangée de teintes fixées à une même luminosité se lit comme une seule rangée, et changer la luminosité laisse la teinte là où vous l'avez mise. L'interpolation peut aussi se faire dans cet espace, comme dans linear-gradient(in oklch, #0000FF, #FFFF00). Une réserve : la luminosité OKLCH est bien plus proche de la clarté apparente que celle de HSL, mais ce n'est pas la formule de luminance des WCAG, et les paires de texte passent donc toujours par un vérificateur de contraste.
Gamut et écrêtage
Un gamut est l'ensemble des couleurs qu'un espace peut représenter. sRGB en est le défaut de longue date ; Display P3 couvre environ un quart de couleurs visibles en plus, le gain portant presque entièrement sur les rouges et les verts profonds. Le chroma en OKLCH n'est pas borné : écrire une couleur qu'aucun écran ne sait montrer est donc facile.
@supports (color: color(display-p3 1 1 1)) {
.vivid { background: color(display-p3 0.9 0.1 0.14); }
}
L'écrêtage canal par canal ramène une couleur hors gamut en fixant chaque canal à sa limite de façon indépendante, ce qui décale la teinte et rabat des nuances voisines sur une seule valeur : les dégradés se strient et le détail disparaît. CSS Color 4 procède plutôt par mise en correspondance, en conservant la luminosité et la teinte et en réduisant le chroma en OKLCH jusqu'à ce que la couleur tienne.
C'est pourquoi une capture d'écran d'une photo éclatante peut paraître fade. La photo porte un profil P3 et l'écran l'affiche pleinement ; un export converti en sRGB comprime ces pixels saturés dans un volume plus petit, et ce sont les rouges et les verts qui ont le plus de chemin à parcourir.
La composition alpha
Une couleur semi-transparente posée sur un fond produit une troisième couleur, opaque, canal par canal :
result = alpha * source + (1 - alpha) * backdrop
Du noir à 50 % sur du blanc donne (127.5, 127.5, 127.5), soit #808080. C'est de cette coïncidence que vient l'idée qu'« un noir à 50 % d'opacité est un gris », et elle ne vaut que sur du blanc : le même noir sur #3B82F6 donne environ #1E417B, un bleu foncé, et sur une photographie il donne une couleur différente à chaque pixel. Le noir semi-transparent est un opérateur d'assombrissement, pas un gris.
La composition s'effectue sur les valeurs encodées : ce #808080 a donc une luminance relative d'environ 0,216 et non de 0,5. Mesurez l'hexadécimal composé, jamais la couleur déclarée. L'alpha ne s'empile pas non plus comme on l'attend : deux couches à 50 % en couvrent 75 %, parce que la seconde n'agit que sur ce que la première a laissé passer.
Teintes claires, teintes foncées, tons rompus et harmonies
| Terme | Recette traditionnelle | En OKLCH |
|---|---|---|
| Teinte claire | base plus blanc | monter L, baisser légèrement C |
| Teinte foncée | base plus noir | baisser L |
| Ton rompu | base plus gris | baisser C à L constant |
Les harmonies sont des décalages de teinte. Elles choisissent un accent ; elles ne construisent pas un système.
| Harmonie | Décalages | Utile pour |
|---|---|---|
| Complémentaire | +180° | un accent face à une teinte dominante, même si l'ensemble vibre quand les deux sont saturées |
| Analogue | ±30° | des regroupements calmes, des arrière-plans, des états d'une même famille |
| Triadique | +120°, +240° | des ensembles catégoriels comme des séries de graphique |
Les décalages sur la roue RGB ne sont pas perceptuellement réguliers, car cette roue consacre un arc disproportionné au vert : traitez donc une harmonie calculée comme un premier jet et ajustez à l'œil. Un générateur de palette qui construit teintes claires, teintes foncées et harmonies à partir d'une seule base met ce premier jet à l'écran rapidement.
La rampe de neutres exige sa propre décision. Un gris pur, rouge, vert et bleu à égalité, posé à côté d'une interface colorée paraît mort et légèrement sale. Donnez aux neutres un petit chroma, de l'ordre de 0,005 à 0,02 en OKLCH, proche de la teinte de la marque : des gris chauds autour de 60° à 90° évoquent le papier, des gris froids autour de 250° font technique. Faites décroître ce chroma vers les paliers les plus clairs pour que les arrière-plans ne semblent pas teintés.
Le daltonisme
Environ 1 homme sur 12 et 1 femme sur 200 présentent une déficience de la vision des couleurs. Les types rouge-vert en représentent la quasi-totalité, la deutéranomalie très largement en tête ; les types bleu-jaune, dits tritan, touchent moins de 1 personne sur 10 000.
Les confusions qui comptent dans une interface sont le rouge face au vert à luminosité voisine, le vert face au brun et à l'orange, le violet face au bleu, et le rose face au gris. La vision protan assombrit en outre les rouges : un rouge choisi pour signaler l'urgence peut finir par être l'élément le plus discret à l'écran.
La couleur ne doit donc jamais être le seul porteur de sens. Associez chaque état à un libellé, une icône, une forme ou une position, et faites en sorte que des états appariés diffèrent en luminosité autant qu'en teinte, car un rouge et un vert de même luminosité OKLCH forment la pire combinaison possible. Convertir une capture d'écran en niveaux de gris repère ces collisions en quelques secondes.