Présentation de base de la façon d'établir un jeu de couleurs dynamique et configurable
Dans cet article, je souhaite partager des réflexions sur la façon de gérer plusieurs jeux de couleurs en CSS. Tester la fonctionnalité
Si vous préférez les vidéos, voici une version YouTube de cet article :
Présentation
Nous allons créer un système de couleurs accessible avec des propriétés personnalisées et calc() pour créer une page Web qui s'adapte aux préférences de l'utilisateur tout en minimisant l'expérience de création. Nous commençons par une couleur de base de la marque et créons un système de variantes à partir de celle-ci : deux couleurs de texte, quatre couleurs de surface et une ombre assortie.
Ce guide commence par définir toutes les couleurs de chaque palette de couleurs. Elles ne sont utilisées pour modifier la page qu'à la toute fin.
La marque
Souvent, une couleur de marque a déjà été établie et est fournie au format hexadécimal ou RGB. La couleur de base de la marque pour ce défi d'interface utilisateur graphique est #0af. Tout d'abord, pour ce système de couleurs, la valeur hexadécimale doit être convertie en hsl.
* {
--brand: #0af;
--brand: hsl(200 100% 50%);
}
Pour activer un concept d'assombrissement ou d'éclaircissement de la couleur de la marque, par exemple de 20 %, les trois canaux de la valeur de couleur HSL doivent être extraits dans leurs propres propriétés personnalisées, comme ceci :
* {
--brand-hue: 200;
--brand-saturation: 100%;
--brand-lightness: 50%;
}
Le CSS peut effectuer des calculs sur ces propriétés de couleur, par exemple calc(var(--brand-lightness) -
20%) pour diminuer la valeur de luminosité de 20 %. C'est un élément fondamental pour créer un schéma de couleurs, car le CSS peut conserver toutes les couleurs dans la même famille de teintes en ajustant les valeurs de saturation et de luminosité HSL.
Thème clair
Chaque variante de couleur sera marquée avec son schéma correspondant. Dans ce cas, chaque variante est suivie de -light.

Marque
En commençant par la couleur de la marque, elle est reconstruite en encapsulant les propriétés personnalisées --brand-hue, --brand-saturation et --brand-lightness à l'intérieur des parenthèses de la fonction hsl (), sans aucun calcul :
* {
--brand-light: hsl(var(--brand-hue) var(--brand-saturation) var(--brand-lightness));
}
Couleurs du texte
Ensuite, les éléments essentiels d'un jeu de couleurs ont besoin de couleurs de texte. Dans un thème clair, le texte doit être très sombre. Notez que la luminosité des couleurs suivantes est faible, bien inférieure à 50 %.
* {
--text1-light: hsl(var(--brand-hue) var(--brand-saturation) 10%);
--text2-light: hsl(var(--brand-hue) 30% 30%);
}
--text1-light, qui est très sombre à 10 % de luminosité, conserve la saturation élevée de 100 % afin que la couleur de la marque puisse encore transparaître dans le bleu marine très foncé.
--text2-light, qui n'est pas aussi sombre que la première couleur, ce qui est bien, car il s'agit d'une couleur secondaire, et qui est également beaucoup moins saturée.
Couleurs de surface
Les couleurs de surface sont les arrière-plans, les bordures et les autres surfaces décoratives sur ou dans lesquelles le texte est placé. Dans un thème clair, il s'agit des couleurs claires, par opposition aux couleurs sombres du texte. Pour créer des couleurs claires avec HSL, nous utiliserons des valeurs de pourcentage plus élevées dans la troisième valeur de luminosité. Nous allons également réduire la saturation pour que les gris clairs ne soient pas trop teintés.
* {
--surface1-light: hsl(var(--brand-hue) 25% 90%);
--surface2-light: hsl(var(--brand-hue) 20% 99%);
--surface3-light: hsl(var(--brand-hue) 20% 92%);
--surface4-light: hsl(var(--brand-hue) 20% 85%);
}
Quatre couleurs de surface ont été créées, car les couleurs décoratives ont tendance à nécessiter plus de variantes, pour les moments interactifs comme :focus ou :hover, ou pour créer l'apparence de couches de papier. Dans ces scénarios, il est agréable de passer de --surface2-light à --surface3-light au survol. Ainsi, un survol entraîne une augmentation du contraste (de 99 % de luminosité à 92 % de luminosité, ce qui le rend plus sombre).
Ombres
Les ombres dans un schéma de couleurs sont un plus, mais elles ajoutent un aspect réaliste à l'effet et l'aident à se démarquer des ombres noires irréalistes. Pour ce faire, la couleur de l'ombre utilisera la propriété personnalisée de teinte, sera légèrement saturée avec la teinte, mais restera très sombre. Il s'agit essentiellement de créer une ombre très sombre, légèrement bleutée.
* {
--surface-shadow-light: var(--brand-hue) 10% 20%;
--shadow-strength-light: .02;
}
--surface-shadow-light n'est pas encapsulé dans une fonction hsl. En effet, la valeur --shadow-strength sera combinée pour créer une certaine opacité, et le CSS a besoin des éléments pour effectuer les calculs. Pour en savoir plus, accédez à la section Ombre radiale.
Toutes les couleurs de lumière
Vous n'avez pas besoin de chercher comment sont créées les couleurs claires, elles sont toutes regroupées dans le CSS.
* {
--brand-light: hsl(var(--brand-hue) var(--brand-saturation) var(--brand-lightness));
--text1-light: hsl(var(--brand-hue) var(--brand-saturation) 10%);
--text2-light: hsl(var(--brand-hue) 30% 30%);
--surface1-light: hsl(var(--brand-hue) 25% 90%);
--surface2-light: hsl(var(--brand-hue) 20% 99%);
--surface3-light: hsl(var(--brand-hue) 20% 92%);
--surface4-light: hsl(var(--brand-hue) 20% 85%);
--surface-shadow-light: var(--brand-hue) 10% calc(var(--brand-lightness) / 5);
--shadow-strength-light: .02;
}
Thème sombre
La plupart des marques ne commencent pas par un thème sombre. Il s'agit d'une variante de leur thème principal, généralement plus clair. Les utilisateurs, en revanche, choisissent souvent un thème sombre pour différents contextes, comme la nuit. Ces facteurs m'ont amené à garder deux choses à l'esprit concernant les thèmes sombres :
- Les utilisateurs seront généralement dans l'obscurité lorsqu'ils utiliseront ce thème. Testez-le donc dans l'obscurité.
- Les couleurs doivent être désaturées pour ne pas vibrer à l'écran en raison de leur intensité excessive.

Marque
Le thème clair utilise les trois valeurs de canaux de couleur HSL de la marque sans les modifier, contrairement au thème sombre. La saturation est divisée par deux et la luminosité est réduite de 50 %.
* {
--brand-dark: hsl(
var(--brand-hue)
calc(var(--brand-saturation) / 2)
calc(var(--brand-lightness) / 1.5)
);
}
Couleurs du texte
Dans un thème sombre, les couleurs du texte doivent être claires. Les couleurs suivantes ont des valeurs de luminosité élevées, ce qui les rapproche du blanc.
* {
--text1-dark: hsl(var(--brand-hue) 15% 85%);
--text2-dark: hsl(var(--brand-hue) 5% 65%);
}
Couleurs de surface
Dans un thème sombre, les couleurs de surface doivent être sombres. Les couleurs suivantes ont une luminosité et une saturation faibles, la première surface étant la plus sombre (10 %).
* {
--surface1-dark: hsl(var(--brand-hue) 10% 10%);
--surface2-dark: hsl(var(--brand-hue) 10% 15%);
--surface3-dark: hsl(var(--brand-hue) 5% 20%);
--surface4-dark: hsl(var(--brand-hue) 5% 25%);
}
Ombres
Dans un thème sombre, les ombres peuvent être très difficiles à voir. Cela a du sens, car il est difficile d'assombrir quelque chose qui est déjà assez sombre. C'est là que --shadow-strength-dark s'avère très utile, car il nous permet d'assombrir les ombres en modifiant une seule variable.
* {
--surface-shadow-dark: var(--brand-hue) 50% 3%;
--shadow-strength-dark: .8;
}
Regardez également le niveau de saturation de cette ombre. Remarquez-vous la couleur lorsque vous regardez l'interface ? Essayez de supprimer la saturation des outils de développement. Quelle option préférez-vous ?
Toutes les couleurs sombres
* {
--brand-dark: hsl(var(--brand-hue) calc(var(--brand-saturation) / 2) calc(var(--brand-lightness) / 1.5));
--text1-dark: hsl(var(--brand-hue) 15% 85%);
--text2-dark: hsl(var(--brand-hue) 5% 65%);
--surface1-dark: hsl(var(--brand-hue) 10% 10%);
--surface2-dark: hsl(var(--brand-hue) 10% 15%);
--surface3-dark: hsl(var(--brand-hue) 5% 20%);
--surface4-dark: hsl(var(--brand-hue) 5% 25%);
--surface-shadow-dark: var(--brand-hue) 50% 3%;
--shadow-strength-dark: .8;
}
Thème sombre
Ce jeu de couleurs consiste à orchestrer la luminosité et la saturation. La saturation doit être suffisante pour que la teinte soit visible, mais doit également juste passer les scores de contraste, car elle est censée être sombre et à faible contraste de toute façon.

Marque
* {
--brand-dim: hsl(
var(--brand-hue)
calc(var(--brand-saturation) / 1.25)
calc(var(--brand-lightness) / 1.25)
);
}
Couleurs du texte
* {
--text1-dim: hsl(var(--brand-hue) 15% 75%);
--text2-dim: hsl(var(--brand-hue) 10% 61%);
}
Couleurs de surface
* {
--surface1-dim: hsl(var(--brand-hue) 10% 20%);
--surface2-dim: hsl(var(--brand-hue) 10% 25%);
--surface3-dim: hsl(var(--brand-hue) 5% 30%);
--surface4-dim: hsl(var(--brand-hue) 5% 35%);
}
Ombres
* {
--surface-shadow-dim: var(--brand-hue) 30% 13%;
--shadow-strength-dim: .2;
}
Diminuer la luminosité de toutes les couleurs
* {
--brand-dim: hsl(var(--brand-hue) calc(var(--brand-saturation) / 1.25) calc(var(--brand-lightness) / 1.25));
--text1-dim: hsl(var(--brand-hue) 15% 75%);
--text2-dim: hsl(var(--brand-hue) 10% 61%);
--surface1-dim: hsl(var(--brand-hue) 10% 20%);
--surface2-dim: hsl(var(--brand-hue) 10% 25%);
--surface3-dim: hsl(var(--brand-hue) 5% 30%);
--surface4-dim: hsl(var(--brand-hue) 5% 35%);
--surface-shadow-dim: var(--brand-hue) 30% 13%;
--shadow-strength-dim: .2;
}
Couleurs accessibles
Notez que la luminosité la plus faible de la couleur du texte sombre est de 65 %, tandis que la luminosité la plus élevée des surfaces sombres est de 25 %. Cela représente une marge de 40 % de luminosité entre les deux. Dans le thème clair, il y a 55 % d'espace libre. Maintenir les différences de luminosité entre les couleurs du texte et de la surface à environ 40 à 50 % peut aider à maintenir des rapports de contraste de couleurs élevés, tout en étant un levier subtil à ajuster en cas de mauvais scores.
Je l'appelle "bump bump til ya pass", qui est l'interaction consistant à augmenter la valeur de luminosité jusqu'à ce qu'un outil indique que je suis conforme.
Chacun des thèmes créés dans ce défi respecte les scores de contraste. Le schéma de couleurs "Sombre" présente le contraste le plus faible, mais respecte tout de même les exigences minimales. Pour aider les autres membres de l'équipe à utiliser des couleurs contrastées, il est judicieux de créer un nom de classe qui associe une couleur de surface à une couleur de texte accessible.
.surface1 {
background-color: var(--surface1);
color: var(--text2);
}
.surface2 {
background-color: var(--surface2);
color: var(--text2);
}
.surface3 {
background-color: var(--surface3);
color: var(--text1);
}
.surface4 {
background-color: var(--surface4);
color: var(--text1);
}
Rad Shadow
Les thèmes utilisent une classe utilitaire appelée .rad-shadow. Cette ombre a été générée avec l'outil Smooth Shadow, que j'apprécie beaucoup. J'ai pris l'extrait généré et je l'ai personnalisé avec mes propres couleurs et calculs d'opacité. L'objectif était de créer une ombre que je pourrais ajuster dans chaque jeu de couleurs.

Pour ce faire, j'ai créé deux variables pour chaque jeu de couleurs à ajuster : une couleur d'ombre et une intensité d'ombre. La couleur permet d'ajuster la saturation et l'obscurité, tandis que l'intensité permet d'augmenter facilement l'intensité de l'ombre lorsqu'il s'agit d'un jeu de couleurs sombres. Le résultat final ressemblait à ceci.
:root {
--surface-shadow-light: var(--brand-hue) 10% 20%;
--shadow-strength-light: .02;
}
.rad-shadow {
box-shadow:
0 2.8px 2.2px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .03)),
0 6.7px 5.3px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .01)),
0 12.5px 10px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .02)),
0 22.3px 17.9px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .02)),
0 41.8px 33.4px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .03)),
0 100px 80px hsl(var(--surface-shadow) / var(--shadow-strength))
;
}
Si je devais aller plus loin avec les ombres dans mon jeu de couleurs, je ferais des angles d'ombre une constante de jeton de conception également, car la direction de la lumière devrait être la même entre toutes les ombres de la conception.
Utilisation des jeux de couleurs
Maintenant que les couleurs sont prédéfinies, il est temps de les transformer en propriétés indépendantes du schéma. En d'autres termes, en tant qu'auteur CSS dans ce projet de jeu de couleurs, il est rare d'avoir besoin d'accéder à la valeur d'un jeu de couleurs spécifique. Je veux qu'il soit facile de rester dans le thème.
Pour ce faire, l'utilisation du jeu de couleurs doit se faire exclusivement par le biais des propriétés personnalisées génériques, que nous allons définir dans un instant. De cette façon, les personnes qui utilisent les variables de conception n'ont jamais à se soucier du schéma de couleurs actuellement défini. Elles n'ont qu'à utiliser les couleurs de surface et de texte. Au lieu de color: var(--text1-light), utilisez color: var(--text1). Toutes les adaptations et les pivots de couleurs sont effectués à un niveau beaucoup plus élevé dans le CSS.
En entrant dans le vif du sujet, les styles de connexion du thème clair dans le bloc de code suivant connectent une propriété personnalisée générique à la couleur spécifique du thème clair. Désormais, toutes les utilisations de var(--brand) utiliseront la couleur claire de la marque.
Thème clair (automatique)
:root {
color-scheme: light;
--brand: var(--brand-light);
--text1: var(--text1-light);
--text2: var(--text2-light);
--surface1: var(--surface1-light);
--surface2: var(--surface2-light);
--surface3: var(--surface3-light);
--surface4: var(--surface4-light);
--surface-shadow: var(--surface-shadow-light);
--shadow-strength: var(--shadow-strength-light);
}
Le site utilise désormais le thème clair. C'est un moment très amusant et réussi ! Nous allons vivre d'autres moments comme celui-ci en utilisant nos couleurs prédéfinies dans d'autres contextes de jeu de couleurs.
Thème sombre (automatique)
@media (prefers-color-scheme: dark) {
:root {
color-scheme: dark;
--brand: var(--brand-dark);
--text1: var(--text1-dark);
--text2: var(--text2-dark);
--surface1: var(--surface1-dark);
--surface2: var(--surface2-dark);
--surface3: var(--surface3-dark);
--surface4: var(--surface4-dark);
--surface-shadow: var(--surface-shadow-dark);
--shadow-strength: var(--shadow-strength-dark);
}
}
Thème clair
[color-scheme="light"] {
color-scheme: light;
--brand: var(--brand-light);
--text1: var(--text1-light);
--text2: var(--text2-light);
--surface1: var(--surface1-light);
--surface2: var(--surface2-light);
--surface3: var(--surface3-light);
--surface4: var(--surface4-light);
--surface-shadow: var(--surface-shadow-light);
--shadow-strength: var(--shadow-strength-light);
}
Thème sombre
[color-scheme="dark"] {
color-scheme: dark;
--brand: var(--brand-dark);
--text1: var(--text1-dark);
--text2: var(--text2-dark);
--surface1: var(--surface1-dark);
--surface2: var(--surface2-dark);
--surface3: var(--surface3-dark);
--surface4: var(--surface4-dark);
--surface-shadow: var(--surface-shadow-dark);
--shadow-strength: var(--shadow-strength-dark);
}
Thème sombre
[color-scheme="dim"] {
color-scheme: dark;
--brand: var(--brand-dim);
--text1: var(--text1-dim);
--text2: var(--text2-dim);
--surface1: var(--surface1-dim);
--surface2: var(--surface2-dim);
--surface3: var(--surface3-dim);
--surface4: var(--surface4-dim);
--surface-shadow: var(--surface-shadow-dim);
--shadow-strength: var(--shadow-strength-dim);
}
À ce stade, les auteurs sont libres d'utiliser les génériques de jeu de couleurs fournis selon leurs besoins et ne devraient plus jamais avoir à se soucier des thèmes.
Conclusion
Maintenant que vous savez comment j'ai fait, comment feriez-vous ? 🙂
Diversifions nos approches et découvrons toutes les façons de créer sur le Web. Créez une démo sur CodePen ou hébergez-la vous-même, tweetez-la moi et je l'ajouterai à la section "Remix de la communauté" ci-dessous.
Source
Remix de la communauté
- @chris-kruining a ajouté un curseur de teinte, des couleurs d'état et des modes de contraste pour no-preference, more et less : démo.