Que fait Formule de palette OKLCH ?
Les neutres d'un produit (fond de page, surface de carte, encarts, filets, quatre niveaux de texte) sont généralement 15 à 30 valeurs hexadécimales par thème, choisies à l'œil. Elles dérivent, le mode sombre est un second ensemble choisi à la main, et personne ne sait quelles paires passent encore le contraste. Remplacez-les par une formule :
token de type surface : L = papier + profondeur × n(token) chroma = teinte hue = teinte
token de type encre : L = encre + échelon × n(token) chroma = teinte-encre hue = teinte
n(token) est la position du token sur une échelle (ladder), une échelle par thème. Déplacez une entrée et toute la rampe se déplace ensemble, de sorte que les écarts entre les échelons, là où se trouve le contraste, survivent à une retinte. Retinter un produit est une seule ligne (--hue).
Trois boutons (--hue, --lightness, --chroma ou --contrast) pilotent chaque couleur.
Fichiers
references/palette.css: règles prêtes à l'emploi avec des entrées et échelles de démarrage.scripts/fit_palette.py: ajuste les entrées à une palette hexadécimale existante et imprime ΔE par token.scripts/ladders.json: les échelles par défaut (clair, sombre).scripts/palette-audit.mjs: résout chaque token dans un vrai navigateur dans les deux thèmes et échoue sur le contraste.scripts/oklch.py: conversions et aides ΔE / WCAG.
Procédure
-
Nommez les tokens par leur rôle, pas par leur valeur. Les valeurs par défaut :
- Fonds :
ground(page),panel(rails, barres),surface(cartes, menus),surface-2(encarts),surface-3(chips, lignes survolées),field. - Lignes :
line,line-2,line-3(filets, du plus clair au plus foncé), plusfield-line, le bord d'un champ, qui doit passer 3:1. - Encre :
ink,ink-2,ink-3,ink-4(texte, du plus foncé au plus discret). Chaque échelon d'encre est du texte, donc chacun doit passer 4.5:1.
- Fonds :
-
Ajustez, ne re-choisissez pas. Pour un produit existant, exécutez
python3 scripts/fit_palette.py theme.css. Il lit les déclarations hexadécimales--color-*(clair, puis la règle[data-theme="dark"]), ajuste les entrées par rapport à l'échelle par défaut, et imprime ΔE par token. En dessous de 2, c'est visuellement identique. -
Remplacez les échelons de l'échelle là où le design les espace différemment, jamais la couleur. Évaluez les candidats avec
fit_palette.py theme.css light '<entrées>' '<remplacements>'jusqu'à ce que le pire ΔE soit inférieur à ~2. Remplacements typiques :field: 0(les champs sont la face de la carte), unline-3plus large, un écart plus grand entreinketink-2. -
Écrivez les règles (voir
references/palette.css) :- Placez les entrées et les formules
--color-*sur l'élément qui porte l'attribut de thème. - Reformulez chaque entrée que vous modifiez dans la règle sombre. Une règle sombre écrite pour les valeurs par défaut du kit gagne sinon avec ses valeurs.
- Avec Tailwind v4, conservez les noms de tokens dans
@themepour que les utilitaires existent ; les déclarations d'exécution les remplacent.
- Placez les entrées et les formules
-
Auditez dans un vrai navigateur :
node scripts/palette-audit.mjs <url>. Il peint chaque token, lit le sRGB renvoyé, et vérifie :- chaque échelon d'encre à 4.5:1 contre chaque fond ;
field-lineetringà 3:1.
Corrigez un ÉCHEC en déplaçant l'échelon de l'échelle de ce token, pas en spécialisant une couleur. Attendez-vous à des ÉCHECs sur les palettes existantes : dans une migration de 17 apps, l'audit a trouvé des valeurs
ink-4oufield-linedéfaillantes dans plus de la moitié d'entre elles, toutes déjà livrées.
Pièges
var()à l'intérieur d'une propriété personnalisée se résout là où elle est déclarée.- Déclarez les formules sur
:rootet définissez les entrées sur un enfant : l'enfant ne se recolorie jamais. - Les tokens alias (
--color-heading: var(--color-ink)) doivent également être déclarés sur le même élément thématisé.
- Déclarez les formules sur
- Les valeurs
@themede Tailwind v4 vivent sur:root. Un thème limité à une racine d'app ([data-app]) doit déclarer la formule là, pas dans@theme. - Tout ce qui transmet des couleurs à un canvas ou une bibliothèque cartographique (
getComputedStyle(...).getPropertyValue('--color-x')) reçoit désormais une chaîneoklch(calc(...))non résolue. Résolvez-la d'abord : peignez un élément sonde et lisez un canvas de 1 px, comme le fait l'audit. - Une seule teinte pour les fonds et les encres. Si les encres du design portent une teinte différente de leurs fonds, gardez
--ink-tintbas ; à L 0.98, toute chroma se lit comme une dominante colorée. - Les couleurs de marque, de statut et de graphiques restent en dehors de la formule. Dérivez les variantes texte-sur-teinte (par exemple
brand-text) de la même teinte à une clarté fixe plutôt que de les choisir séparément. - Les palettes à teinte mixte, uniquement claires, échantillonnées à partir de maquettes peuvent ne pas rentrer dans ~2 ΔE. Ignorez-les plutôt que de déformer le design. L'audit s'applique toujours.