Addon · Couleurs

Une teinte. Seize nuances.

Couleurs propulsées OKLCH avec variantes automatiques d'opacité, de luminosité et d'obscurité. Faites glisser les balles du logo pour remixer la teinte brand.

Teinte en direct

Trois nombres — trois teintes — génèrent tout le système ci-dessous.

Teinte Brand 169° --brand-hue
Teinte Accent 10° --accent-hue
Teinte Extra 200° --extra-hue

Toutes les nuances

5 couleurs de base × 8 variantes. Même schéma de nommage, à chaque fois.

Brand
brand
brand_
brand__
brand___
brand-d
brand-b
brand-d_
brand-b_
Accent
accent
accent_
accent__
accent___
accent-d
accent-b
accent-d_
accent-b_
Extra
extra
extra_
extra__
extra___
extra-d
extra-b
extra-d_
extra-b_
Sombre / Clair
dark
dark_
dark__
dark___
light
light_
light__
light___
Convention de nommage
_c-brand     /* couleur */
_bg-brand    /* fond */
_bc-brand    /* bordure */
Modificateurs d'opacité
brand     /* plein */
brand_    /* 65% */
brand__   /* 35% */
brand___  /* 15% */
Modificateurs de luminosité
brand-d   /* plus sombre */
brand-b   /* plus clair */
brand-d_  /* sombre profond */
brand-b_  /* clair vif */

Sombre / Clair

Un pivot, deux variables. Les mêmes tokens --dark et --light permutent leur luminosité automatiquement — les composants restent agnostiques au mode. OKLCH garde le chroma et la teinte intacts ; seul le canal L bascule.

Le pivot

Deux variables CSS pilotent l'inversion complète du mode. Tout le reste lit depuis elles.

/* Mode clair (défaut) */
--current-lightness: 96.4%;
--current-darkness:   6.4%;

/* Mode sombre — quand .toggle-theme est actif */
--current-lightness:   5%;
--current-darkness:  95%;
Comment --dark et --light se branchent

--dark et --light référencent le pivot. Inversez le pivot, tous les consommateurs basculent d'un coup — même chroma et même teinte OKLCH, luminosité inversée. Ce sont les défauts de :root ; un thème surcharge le chroma et la teinte.

--dark-lightness:  var(--current-darkness);
--dark-chroma:     0.05;
--dark-hue:        var(--brand-hue);

--light-lightness: var(--current-lightness);
--light-chroma:    0.1;
--light-hue:       var(--brand-hue);

Même markup, mêmes tokens. Seuls --current-lightness et --current-darkness changent entre les deux cartes.

Mode clair (forcé)
light
dark
brand
Mode sombre (forcé)
light
dark
brand
Bascule manuelle

Ajoutez la classe toggle-theme sur le body. EVA inverse aussi les offsets de luminosité, donc le modificateur -d reste assombrissant et -b reste éclaircissant à l'œil.

<body class="current-theme theme-eva toggle-theme">
Auto (prefers-color-scheme)

Passez $auto-theme-switch: true à la config SCSS. EVA génère une media query prefers-color-scheme: dark qui inverse le pivot pour vous — pas de JS, pas de classe.

@use 'eva-css-fluid' with (
  $auto-theme-switch: true
);
La règle

Écrivez _c-dark _bg-light et oubliez les modes. Le pivot s'occupe du reste — votre code de composant reste identique dans les deux états.

Essayez — cliquez l'icône soleil en haut à droite de cette page. Toutes les nuances ci-dessus basculent d'un coup.

Nouveau en 2.4

Luminosité par rôle

Les quatre crans de luminosité étaient quatre décalages globaux partagés par les cinq bases. Chaque base peut désormais surcharger les siens — et passer en cran proportionnel, qui ne sature jamais.

Deux crans, une seule couleur

La lightness OKLCH est écrêtée à 0–100 %. Avec --light-lightness à 96,4 %, les deux crans clairs tombent exactement sur le même blanc.

--light-b   = 96.4% + 10%  = 106.4%  →  100%
--light-b_  = 96.4% + 30%  = 126.4%  →  100%

Et c'est symétrique : en mode sombre, --dark-d et --dark-d_ s'effondrent ensemble sur le noir. Sur chaque neutre, dans chaque mode, 2 des 4 crans étaient inutilisables — précisément sur dark et light, les deux bases les plus sollicitées.

La formule

Chaque cran lit d'abord son token de rôle, puis retombe sur le token global. Le terme proportionnel est opt-in : le ratio vaut 0 par défaut, donc son terme s'annule et le comportement absolu ne bouge pas.

lightness = base
          + décalage absolu
          + (butée − base) × ratio

La butée est la limite vers laquelle le cran pousse — 100 % pour un cran éclaircissant, 0 % pour un cran assombrissant. Elle s'inverse avec le mode, comme les décalages.

Absolu contre proportionnel

Même base, mêmes deux crans. À gauche les décalages absolus par défaut, à droite les mêmes crans passés en proportionnel. Basculez le thème — ça tient dans les deux modes.

Absolu — le défaut
light
light-b
light-b_

Les deux crans clairs saturent. Une seule couleur, affichée deux fois.

Proportionnel — opt-in
light
light-b
light-b_

Chaque cran prend une part de la marge restante. Toujours distincts, toujours dans le gamut.

Chaque nuance affiche la lightness OKLCH réellement peinte, relue depuis le navigateur. La carte de droite porte quatre custom properties en inline — rien d'autre ne change, même markup, mêmes classes.

--light-brighter:  0%;  --light-brighter-ratio:  .35;
--light-brighter_: 0%;  --light-brighter_-ratio: .7;
Les tokens

<base> vaut brand, accent, extra, dark ou light. <token> vaut darker, brighter, darker_ ou brighter_.

--<base>-<token>         /* décalage absolu par rôle */
--<base>-<token>-ratio   /* part proportionnelle par rôle */
--<base>-<token>-bound   /* butée visée par rôle */

--<token>-ratio           /* global, vaut 0 par défaut */
--<token>-bound           /* global, suit le mode */
Recettes
Neutres resserrés, accent large

Le cas le plus courant : les neutres veulent des pas de 2 à 4 points pour rester lisibles, l'accent en veut 12 à 30 pour marquer ses états.

.current-theme {
  --dark-darker:  -2%;
  --dark-brighter: 4%;

  --accent-brighter_: 12%;
}

Seul le cran nommé bouge. --dark-b_ continue d'utiliser le --brighter_ global.

Des crans qui ne saturent jamais

On met la part absolue à 0 pour passer en proportionnel pur. Les deux crans restent distincts et dans le gamut, dans les deux modes.

.current-theme {
  --light-brighter:  0%;
  --light-brighter-ratio:  .35;

  --light-brighter_: 0%;
  --light-brighter_-ratio: .7;
}

C'est exactement ce que fait la carte de démo ci-dessus.

Un plancher fixe plus une part

Les deux termes s'additionnent. Utile pour garantir un écart minimum tout en suivant la marge réellement disponible.

.current-theme {
  --dark-darker: -2%;
  --dark-darker-ratio: .3;
}

-2 % garantis, plus 30 % de la distance à la butée.

Trois choses à savoir

--darker est positif en mode sombre. Ce n'est pas un bug : en mode sombre l'encre dark est claire (95 %), et -d veut dire plus de contraste avec le fond, pas plus sombre dans l'absolu. Les crans décrivent une relation — c'est aussi pour ça que les butées s'inversent avec le mode.

--dark-hue et --light-hue valent var(--brand-hue) par défaut, avec un chroma de 0.05 et 0.1. L'encre et le fond sont donc teintés par la marque — c'est un choix assumé, mais ça surprend : on change --brand-hue et tous les textes virent avec. Passez les chroma à 0, ou posez les hues explicitement. Un thème généré le fait déjà pour vous.

Les fondus sont figés au build. _, __ et ___ sortent du compilateur avec 65 %, 35 % et 15 % inlinés. $fade-values est surchargeable en SCSS, pas à l'exécution, et les deux axes ne se croisent pas : il n'existe pas de --dark-d à 35 %.

Rien n'a bougé

20 lignes changées dans le CSS émis — les 5 bases × 4 crans, rien d'autre. Les 20 variantes ont été comparées en getComputedStyle avant et après, en mode clair et en mode sombre : zéro dérive. Tout tient sur le repli natif de var() — tant qu'un token par rôle n'est pas défini, la chaîne retombe sur la valeur globale d'origine.

Référence complète

Toutes les variantes, toutes les formules — dans /llms.txt.

Bases OKLCH, switching de thème, formules de contraste. Plus l'outil auto-theme pour extraire des palettes depuis des images.