Temps de lecture: 7 minutes
À présent, vous savez probablement que vous pouvez utiliser des propriétés personnalisées pour des composants de couleur individuels, afin d’éviter de répéter plusieurs fois les mêmes coordonnées de couleur dans votre thème. Vous savez peut-être même que vous pouvez utiliser la même variable pour plusieurs composants, par exemple la teinte et la luminosité HSL :
:root {
--primary-hs: 250 30%;
}
h1 {
color: hsl(var(--primary-hs) 30%);
}
article {
background: hsl(var(--primary-hs) 90%);
}
article h2 {
background: hsl(var(--primary-hs) 40%);
color: white;
}
Voici une page très simple conçue avec cette technique :
Contrairement aux variables de préprocesseur, vous pouvez même remplacer localement la variable, pour avoir des blocs avec une couleur d’accent différente :
:root {
--primary-hs: 250 30%;
--secondary-hs: 190 40%;
}
article {
background: hsl(var(--primary-hs) 90%);
}
article.alt {
--primary-hs: var(--secondary-hs);
}
Tout va bien jusqu’à ce que le mode sombre entre en jeu. L’idée d’utiliser des propriétés personnalisées pour faciliter l’adaptation d’un thème au mode sombre n’est pas nouvelle. Cependant, dans chaque article que j’ai vu, la stratégie suggérée consiste à créer un ensemble de propriétés personnalisées, une pour chaque couleur, et à les remplacer dans une requête multimédia.
C’est une bonne approche, et vous voudrez probablement le faire pour au moins une partie de vos couleurs éventuellement. Cependant, même dans les conceptions les plus disciplinées, toutes les couleurs ne sont pas une variable CSS. Vous avez souvent des couleurs déclarées en ligne, notamment des gris (par exemple la couleur du pied de page dans notre exemple). Cela signifie que l’ajout d’un mode sombre est suffisamment éprouvant pour que vous puissiez le reporter à plus tard, en particulier sur des projets parallèles.
L’astuce que je vais vous montrer fera grincer des dents à tous ceux qui en savent assez sur la couleur (désolé Chris !) Mais cela vous aide à créer un mode sombre qui travaux en minutes. Ce ne sera pas génial, et vous devriez éventuellement le modifier pour créer un mode sombre approprié (le mode sombre ne consiste pas seulement à échanger des couleurs), mais c’est mieux que rien et peut servir de base.
L’idée de base est d’utiliser des propriétés personnalisées pour légèreté de couleurs au lieu de la couleur entière. Ensuite, en mode sombre, vous remplacez ces variables par 100% - lightness
. Cela produit généralement des couleurs claires pour les couleurs sombres, des couleurs moyennes pour les couleurs moyennes et des couleurs sombres pour les couleurs claires, et vous permet toujours de définir des couleurs en ligne, au lieu de vous forcer à utiliser une variable pour chaque couleur. Voici à quoi ressemblerait le code pour notre exemple :
root {
--primary-hs: 250 30%;
--secondary-hs: 190 40%;
--l-0: 0%;
--l-30: 30%;
--l-40: 40%;
--l-50: 50%;
--l-90: 90%;
--l-100: 100%;
}
@media (prefers-color-scheme: dark) {
:root {
--l-0: 100%;
--l-30: 70%;
--l-40: 60%;
--l-90: 10%;
--l-100: 0%;
}
}
body {
background: hsl(0 0% var(--l-100));
color: hsl(0 0% var(--l-0));
}
h1 {
color: hsl(var(--primary-hs) var(--l-30));
}
article {
background: hsl(var(--primary-hs) var(--l-90));
}
article h2 {
background: hsl(var(--primary-hs) 40%);
color: white;
}
footer {
color: hsl(0 0% var(--l-40));
}
Le résultat ressemble à ceci en mode clair et sombre :
Notez qu’ici nous avons remplacé indifféremment toutes les luminosités par des variables de luminosité. En réalité, nous n’avons pas besoin d’être aussi rapides. Par exemple, les titres des articles seraient en fait plus beaux et auraient un meilleur contraste si nous les gardions les mêmes :
Ce sont des décisions faciles à prendre lorsque vous parcourez votre CSS en remplaçant les pourcentages de luminosité par des variables et en prévisualisant le résultat.
Le problème avec HSL
Mais pourquoi les en-têtes d’articles étaient-ils plus lisibles avec leurs couleurs d’origine qu’avec une légèreté inversée ? La cause profonde est que la légèreté HSL ne correspond pas réellement à ce que les humains perçoivent comme légèreté, et la même différence de légèreté peut produire des différences de perception très différentes.
C’est le gros problème avec cette approche : elle suppose que la légèreté HSL signifie réellement quelque chose, mais comme nous en avons déjà discuté, ce n’est pas le cas. Le jaune et le bleu ont la même légèreté HSL (50%) pour crier fort ! De plus, vous remarquerez que vos couleurs sombres ont de plus petites différences entre elles que vos couleurs claires, car HSL n’est pas perceptiblement uniforme.
Cela signifie-t-il que la technique n’est pas utile pour autre chose qu’un espace réservé pendant que nous développons notre réel mode sombre, si c’est ça ?
Eh bien, les choses ne sont pas assez aussi sinistre.
Bientôt, nous aurons les couleurs LCH dans le navigateur. La première implémentation de navigateur vient d’être livrée dans Safari et il y a également une activité dans cet espace parmi les autres fournisseurs de navigateurs.
LCH est un bien meilleur espace colorimétrique pour cette technique, car sa légèreté signifie en fait quelque chose, non seulement à travers différentes luminosités de la même couleur, mais à travers différentes teintes et chromas.
Cet exemple suivant nécessite Safari TP 120+ . Comparez ces deux dégradés, celui du haut montrant diverses couleurs HSL toutes avec une luminosité de 50 %, et celui du bas diverses couleurs LCH, toutes avec une luminosité de 50 %. Vous pouvez même régler le curseur et essayer différentes luminosités :
Voici une capture d’écran pour ceux d’entre vous qui n’ont pas accès à Safari TP 120+ :
Notez qu’en HSL, certaines couleurs (comme le jaune et le cyan) sont beaucoup plus claires que d’autres. Dans LCH, toutes les couleurs à la même luminosité sont, eh bien, la même luminosité.
Gardez à l’esprit que le chroma LCH ne correspond pas vraiment à la luminosité HSL, donc même si nous l’avons réglé sur le même nombre, cela ne correspond pas à la même chose.
Alors, comment cette technique fonctionnerait-elle avec les couleurs LCH ? Essayons-le !
J’ai utilisé cet outil pour convertir les couleurs HSL existantes en LCH, puis j’ai légèrement modifié les valeurs manuellement car les couleurs initialement converties n’étaient pas belles dans toutes les luminosités LCH (notez que les couleurs HSL avec la même teinte et la même saturation peuvent avoir une teinte et chromas dans LCH. Le contraire irait à l’encontre du point !). Voici à quoi ressemble cette technique avec les couleurs LCH à la place (vous aurez besoin de Safari TP 120 ou version ultérieure pour voir ceci):
Et voici une capture d’écran :
Non seulement le mode sombre est beaucoup mieux, mais même en mode clair, nos deux couleurs alternatives semblent en fait plus uniformes car elles ont la même luminosité LCH.
Voici une comparaison des deux modes sombres :
Ici, vous pouvez voir une comparaison animée entre eux:
Notez qu’en réalité, jusqu’à ce que les couleurs LCH soient prises en charge de manière fiable partout, vous devrez fournir une solution de secours via @supports
mais par souci de brièveté, je n’en ai pas inclus dans cette démo.
Automatisation de la génération des variables de luminosité
Si vous utilisez un préprocesseur qui supporte les boucles, tel que Sass, vous pouvez automatiser la génération de ces variables, et les rendre encore plus granulaires, par exemple tous les 5 % :
:root {
@for $i from 0 through 20 {
--l-#{$i * 5}: #{$i * 5}%;
}
}
@media (prefers-color-scheme: dark) {
:root {
@for $i from 0 through 20 {
--l-#{$i * 5}: #{100 - $i * 5}%;
}
}
}
Pouvons-nous rendre les variables de luminosité plus SÈCHES ?
Certains d’entre vous n’ont peut-être pas aimé la répétition des valeurs : nous devons déclarer par exemple --l-40
à 40 %, puis réglez-le sur 60 % en mode sombre. Ne pouvons-nous pas le dériver d’une manière ou d’une autre, en soustrayant la valeur que nous avons déjà de 100 % ?
Ceux qui ont de l’expérience en programmation peuvent essayer quelque chose comme ceci :
--l-40: calc(100% - var(--l-40));
Cependant, cela ne fonctionnera pas. CSS n’est pas un langage impératif. Il n’a pas d’étapes de calcul, où les variables ont des valeurs différentes avant et après chaque étape. Un tel concept de temps n’existe pas, toutes les déclarations actuellement appliquées doivent être vraies en même temps. Cela ressemble plus à l’évaluation réactive des formules de feuille de calcul qu’au calcul dans JS et d’autres langages de programmation populaires (il existe des langages de programmation réactifs à usage général, mais ils sont moins connus). Par conséquent, les déclarations comme celle ci-dessus sont considérées comme des cycles : puisque --l-40
ne peut pas se référer à lui-même, c’est une erreur, et --l-40
serait défini sur sa valeur initiale en tant que mécanisme de récupération d’erreur (puisque CSS ne peut pas générer d’erreurs).
Alors, existe-t-il un moyen d’éviter de déclarer deux fois les variables de luminosité, une fois pour le mode clair et une fois pour le mode sombre ?
Là est, mais je ne le recommanderais pas. Cela rend le code plus compliqué à lire et à comprendre, pour peu d’avantages. Mais par souci d’amusement intellectuel, je vais le décrire ici.
Au lieu de fixer --l-40
à 40 %, nous allons le fixer en fonction de son différence de 50%c’est à dire -10%
. Alors, calc(50% + var(--l-40))
nous donne 40% et calc(50% - var(--l-40))
nous donne 60%, les deux valeurs dont nous avons besoin. On peut donc déclarer une variable qui est -1
en mode sombre et 1
en mode clair, et multipliez simplement avec cela.
Voici un sous-ensemble de ce à quoi ressemblerait notre code avec ceci :
:root {
--dm: 1;
/* Example declaration: */
--l-40: -10%;
}
@media (prefers-color-scheme: dark) {
:root {
--dm: -1;
}
}
/* Example usage: */
footer {
color: hsl(0 0% calc(50% + var(--dm) * var(--l-40));
/* Ewww! */
}
Et j’espère que vous comprenez maintenant pourquoi je ne le recommanderais pas : usage beaucoup plus compliqué, pour assécher quelques déclarations qui ne seraient spécifiées qu’une seule fois. C’est ce genre d’adhésion obsessionnelle à DRY dont les programmeurs finissent par réaliser qu’elle est contre-productive.
Vous avez aimé cet article ? Inscrivez-vous à mon atelier Smashing sur Dynamic CSS pour plus de contenu comme celui-ci !