Un jeton (token) est la mise en variable du langage de conception : couleurs, tailles de police, espacements, angles arrondis, ombres et durées d’animation sont nommés en variables CSS --lh-* ; les composants ne consomment que des noms, jamais de valeur codée en dur. Changer de peau, c’est changer de jetons.
Le mode sombre est une couche de surcharge, et non un jeu indépendant : on n’écrit que les 118 entrées qui doivent se retourner, les autres conservent la valeur claire.
L’usage des variables SCSS est de participer aux calculs de compilation : le mélange de couleurs (mix() / shade()), la composition des ombres (l’addition de plusieurs couches de shadow), ce que les variables CSS ne peuvent pas faire. Le prix à payer est la cuisson à la compilation — aucun retournement automatique en mode sombre.
Les jetons sont répartis en trois paliers : les deux premiers dans les fichiers globaux du thème, le troisième dans les styles propres de chaque composant :
// 1. Les variables SCSS (variables.scss, à la compilation)
$lh-primary: #2B4C7C!default;
$lh-text-primary: #2C2C2C!default;
// 2. Les variables CSS globales (le :root de var.scss, 190 entrées)
--lh-primary-color: #{$lh-primary};
--lh-text-primary: #{$lh-text-primary};
--lh-bg-color: #FAF6F0;
// 3. Les variables au niveau du composant (le scss du composant lui-même ; la portée est la racine BEM du composant)
Entre les paliers 1 et 2, il s’agit d’une interpolation à la compilation, et non d’une chaîne de références var() : la valeur de --lh-primary-color est cuite à partir de $lh-primary au moment de la construction ; modifier la variable SCSS à l’exécution est sans effet (il faut recompiler). Il n’y a pas de couche intermédiaire « palette de base » dans le projet — pas de variables de gamme de couleurs du type --lh-blue-6 ; les jetons sémantiques sont directement générés à partir des variables SCSS.
Les deux leviers du changement de peau : modifier la variable SCSS de variables.scss et recompiler (cela affecte tous les jetons sémantiques), ou surcharger à l’exécution les variables CSS du :root (c’est la voie qu’empruntent les themeVars du ConfigProvider, qui prend effet immédiatement).
Le palier 3 référence le palier 2 par défaut : la valeur par défaut d’une variable au niveau du composant est généralement une référence var() vers un jeton global (avec repli), ce qui maintient la liaison avec le thème tout en permettant à themeVars de surcharger un seul composant de façon ciblée.
(Les données sont générées par scripts/gen-docs-data.mjs en analysant les trois fichiers de jetons, avec des assertions de quantité : 190 clairs, 118 sombres, 188 SCSS ; tout écart fait échouer.)
// Le scss d'entrée du projet ; la surcharge se fait avant le @use
$lh-primary-color: #1677ff;
@use'@lh-ui/share/styles';
La sémantique de !default est « ne pas surcharger si une valeur existe déjà » ; il suffit donc d’assigner avant le @use. À noter : cela n’affecte que la partie cuite à la compilation ; les variables CSS à l’exécution doivent encore être surchargées par ailleurs.
Le « chantier des jetons de hauteur » a été livré (ajout des --lh-control-height-mini/small/medium/large/x-large,
button / select / input-number / cascader / tree-select / date-picker / time-picker /
input référencent tous les jetons, input passe d’une hauteur dérivée du padding à une hauteur fixe, et le padding-top: 16px asymétrique
des has-label, le décalage fixe de la coche de checkbox, ou encore la répétition codée en dur
du translate(-50%,-50%) de switch ont été corrigés en même temps). Ce qui reste à traiter :
Les paliers de taille ne sont pas alignés entre spinner et icon (small 16 contre 14, medium 24 contre 16, etc.). Modifier un côté ou l’autre impacte toute la bibliothèque (message / notification / loading / toutes les icônes) ; déclencheur : une remontée visuelle de mélange LhIcon / LhSpinner dans un même emplacement ; le cas échéant, un sujet dédié sera ouvert.
Les paliers de size sont inégaux : select / switch n’ont que 3 paliers, le validator de date / time-picker est verrouillé, tree-select / rate / segmented / pagination n’ont pas de size, et mini / x-large sont presque jamais tracés dans la couche CSS (sur toute la bibliothèque, seul avatar possède x-large). C’est un changement de la couche API, découplé des jetons de hauteur.
Un composant n’a pas le droit de déclarer lui-même un --lh-* : il ne peut que consommer via var(--lh-*, fallback) (ligne rouge du AGENTS.md racine). Les nouveaux jetons s’ajoutent dans var.scss.
Le fallback est obligatoire : var(--lh-primary-color, #1677ff) — en l’absence du jeton, une valeur par défaut acceptable subsiste ; la vérification 2 de verify:styles (var fallback) veille.
L’exhaustivité du mode sombre : pour tout nouveau jeton, se demander « doit-il se retourner en mode sombre ? » et, si oui, ajouter la surcharge dans var-dark.scss ; la vérification 7 de verify:styles veille.
Un jeton dont la valeur est de la forme #{$lh-foo} est cuit à la compilation : il ne se retourne pas automatiquement en mode sombre et doit être explicitement redirigé (voir Mode sombre).