Les couleurs relatives en CSS 🎨
Je ne sais pas vous, mais une chose qui m’a toujours frustré en tant que développeur front-end, c’est de devoir manipuler des couleurs.
Appliquer une opacité, ajuster une teinte ou modifier la luminosité d’une couleur selon un contexte donné a toujours été un exercice délicat. 😕
Pendant très longtemps, l’unique solution consistait à utiliser un préprocesseur tel que Sass, proposant diverses fonctions liées aux couleurs (darken()
, lighten()
, etc.).
Puis, l’arrivée des propriétés personnalisées a marqué un tournant majeur dans la conception des interfaces web.
Néanmoins, manipuler une couleur restait encore et toujours un processus laborieux… et ce, jusqu’à l’arrivée des couleurs relatives, introduites par le CSS Color Module Level 5 du W3C ! 🎉
Mais, avant d’entrer dans le vif du sujet, je vous propose de faire un bref retour en arrière sur la façon de manipuler une couleur, à une époque, pas si lointaine, afin de mieux appréhender les problématiques qui y sont liées. 🙄
Section intitulée le-monde-d-avantLe monde d’avant 🦖
Commençons par définir la couleur principale de notre thème à l’aide d’une propriété personnalisée, laquelle est définie globalement via la pseudo-classe :root
:
:root {
--brand-color: #e23e57;
}
Utilisons maintenant cette propriété personnalisée pour modifier la couleur de fond d’un bouton :
.button {
background-color: var(--brand-color);
}
Jusqu’ici tout va bien ! 😇
Nous souhaitons désormais changer l’apparence de notre bouton lorsqu’il est survolé par un pointeur (état :hover
).
Une bonne pratique est d’ajuster la luminosité de la couleur de fond du bouton afin de la rendre plus sombre :
Afin de corser l’exercice, la couleur de fond du bouton survolé doit être relative à sa couleur de fond courante (il n’est donc pas question d’ajouter arbitrairement une nouvelle couleur de fond spécifique à l’état :hover
).
Vous me voyez venir ? Oui, les problèmes commencent maintenant ! 😈
Section intitulée problematiquesProblématiques
Afin de répondre à cette problématique, une des solutions consiste à utiliser l’espace colorimétrique hsl()
, puis, à définir chaque composante de cette couleur (teinte, saturation et luminosité) au sein d’une propriété personnalisée dédiée, comme ceci :
:root {
--brand-color-hue: 351deg;
--brand-color-saturation: 74%;
--brand-color-lightness: 56%;
}
Nous pouvons alors modifier la composante de luminosité de la couleur de fond de notre bouton, indépendamment de ses deux autres composantes (teinte et saturation) :
.button {
background-color: hsl(
var(--brand-color-hue)
var(--brand-color-saturation)
var(--brand-color-lightness)
);
&:hover {
--brand-color-lightness: 41%;
}
}
Oui, mais…
Cette approche présente plusieurs inconvénients : ⚠️
- Nous sommes dépendants de la notation fonctionnelle
hsl()
; - La composante de luminosité a été fixée arbitrairement à 41% (-15%), sans tenir compte de sa valeur initiale définie à 56% ;
- Plus généralement, la complexité du code augmente significativement. Imaginez un projet comportant une dizaine de couleurs et des composants d’interface ayant plusieurs variations (bouton primaire, secondaire, etc.). Nous devrons gérer chaque composante de couleur séparément, et ce, pour chaque couleur et chaque variation de composant d’interface. 🙃
Heureusement, il existe une solution beaucoup plus simple ! 🎉
Section intitulée bienvenue-aux-couleurs-relativesBienvenue aux couleurs relatives 🤗
Définissons d’abord ce qu’est une couleur relative :
Une couleur relative est une couleur basée sur une couleur existante. 🤯
Je sens la déception dans votre regard…
Section intitulée syntaxeSyntaxe
La syntaxe pour définir une couleur relative est assez simple :
color-function(from origin-color channel1 channel2 channel3 / alpha)
Plongeons un peu plus dans les détails :
color-function()
est la notation fonctionnelle utilisée pour manipuler les couleurs, commergb()
,hsl()
,hwb()
, etc. ;origin-color
est, comme son nom l’indique, la couleur d’origine, elle doit être précédée du mot-cléfrom
. La couleur relative se basera sur cette couleur ;chanel1
,chanel2
etchanel3
correspondent aux composantes de la couleur de sortie. Ces composantes sont liées à la notation fonctionnellecolor-function()
utilisée pour la couleur relative ;alpha
est une valeur optionnelle qui représente le canal alpha (opacité) de la couleur.
Voici un premier exemple pour illustrer ça :
.button {
background-color: rgb(from red r g b);
}
Dans cet exemple, notre couleur relative est définie avec la notation fonctionnelle rgb()
.
Les valeurs des composantes de sortie doivent donc suivre cet espace colorimétrique.
La couleur d’origine est, quant à elle, définie par le mot-clé red
.
Il est intéressant de noter que la couleur d’origine est nommée, alors que les composantes de la couleur de sortie suivent l’espace colorimétrique rgb()
.
En réalité, le navigateur convertit la couleur d’origine dans l’espace colorimétrique spécifié pour la couleur relative.
Enfin, les composantes de notre couleur de sortie restent inchangées (r g b
) et sont donc identiques à celles de la couleur d’origine :
- La valeur de la composante rouge
r
de notre couleur de sortie est égale à 255 ; - La valeur de la composante verte
g
de notre couleur de sortie est égale à 0 ; - La valeur de la composante bleue
b
de notre couleur de sortie est égale à 0.
Le code CSS précédent est équivalent à celui-ci :
.button {
background-color: red;
}
Oui, tout ça pour ça. 😅
Section intitulée quelques-exemples-interessantsQuelques exemples intéressants
Maintenant que vous avez saisi le principe, jouons au jeu des différences (indice, il y en a 2 🙄) :
.button {
background-color: rgb(from red b g r);
}
Cette fois-ci, les composantes r
et b
de notre couleur de sortie ont été inversées (le filou).
Oui, il est possible de permuter (et même de répéter) les composantes de la couleur de sortie.
À votre avis, quelle sera la couleur de fond du bouton… ? 🥁
Un indice, l’exemple précédent est équivalent à :
.button {
background-color: rgb(from rgb(255 0 0) b g r);
}
Ou encore :
.button {
background-color: rgb(0 0 255);
}
Soit :
.button {
background-color: blue;
}
Et oui, la couleur de fond du bouton sera donc bleue !
Amusons-nous maintenant à modifier la valeur d’une des composantes de notre couleur de sortie :
.button {
background-color: rgb(from red r g 255);
}
Cette fois-ci, nous avons défini la valeur de la composante bleue b
de notre couleur de sortie à 255.
Ce code CSS est donc équivalent à :
.button {
background-color: rgb(255 0 255);
}
Soit du rouge pourpre… du magenta pour les puristes, ou du violet pour moi 😅 :
.button {
background-color: magenta;
}
Ou encore (là, c’est juste pour mon plaisir personnel) :
.button {
background-color: rgb(from red r g r);
}
C’est magique, non ? 🧙
Section intitulée utilisation-des-proprietes-personnaliseesUtilisation des propriétés personnalisées
Comme nous l’avons mentionné précédemment, il est possible d’utiliser un espace colorimétrique différent entre la couleur d’origine et les composantes de la couleur de sortie.
Mais ce n’est pas tout, la couleur d’origine peut également être stockée dans une propriété personnalisée, ou être spécifiée à l’aide du mot-clé currentcolor
.
Pour reprendre notre tout premier exemple, cette écriture est donc tout à fait envisageable :
:root {
--brand-color: #e23e57;
}
.button {
background-color: hsl(from var(--brand-color) h s 41%);
}
Nous avons modifié la valeur de la composante de luminosité de la couleur de sortie, définie dans un espace colorimétrique hsl()
, à partir de sa couleur d’origine, en notation hexadécimale, elle-même stockée dans une propriété personnalisée. 🤯
Oui, ça commence à être intéressant, mais le meilleur est à venir ! 😄
Section intitulée utilisation-de-la-fonction-code-calc-codeUtilisation de la fonction calc()
Une fonctionnalité vraiment pratique serait de pouvoir modifier les valeurs des composantes de notre couleur de sortie, en se basant sur leurs valeurs initiales, qu’en pensez-vous ?
Bonne nouvelle, cette fonctionnalité est rendue possible grâce à la fonction calc()
! 🎉
Imaginons maintenant que nous souhaitions diminuer la valeur de la composante de luminosité de 15%, par rapport à celle de notre couleur d’origine ?
C’est maintenant possible en une seule déclaration CSS :
.button {
background-color: hsl(from var(--brand-color) h s calc(l - 15));
}
Et oui, tout simplement. 🙂
Notre tout premier exemple peut donc se simplifier de cette façon.
Avant :
:root {
--brand-color-hue: 351deg;
--brand-color-saturation: 74%;
--brand-color-lightness: 56%;
}
.button {
background-color: hsl(
var(--brand-color-hue)
var(--brand-color-saturation)
var(--brand-color-lightness)
);
&:hover {
--brand-color-lightness: 41%;
}
}
Après :
:root {
--brand-color: #e23e57;
}
.button {
background-color: var(--brand-color);
&:hover {
background-color: hsl(from var(--brand-color) h s calc(l - 15));
}
}
Nous avons résolu nos trois problématiques de départ, à savoir :
- Nous sommes libres d’utiliser n’importe quel espace colorimétrique pour définir nos couleurs ;
- Il est désormais possible de modifier la valeur d’une composante de la couleur de sortie en se basant sur sa valeur initiale à l’aide de la fonction
calc()
; - Le code CSS est beaucoup plus simple et facile à maintenir.
Section intitulée bonus-un-bouton-a-plusieurs-variationsBonus : Un bouton à plusieurs variations ?
En apportant quelques modifications à notre exemple précédent, il est même possible, en quelques lignes, d’ajouter des variations à notre bouton 🤩 :
.button--primary {
--button-bg: #e23e57;
}
.button--secondary {
--button-bg: #48a6a7;
}
.button--tertiary {
--button-bg: #7f55b1;
}
.button {
background-color: var(--button-bg);
&:hover {
background-color: hsl(from var(--button-bg) h s calc(l - 15));
}
}
Section intitulée support-navigateurSupport navigateur
Une bonne nouvelle n’arrive jamais seule : Le support navigateur est très correct.
Cette fonctionnalité est supportée par les dernières versions de Chrome, Firefox, Edge, Safari et Opera, raison de plus de l’adopter dès maintenant ! 🥳
Section intitulée le-mot-de-la-finLe mot de la fin
Bref, vous l’aurez compris, les couleurs relatives font parties des fonctionnalités que j’attendais depuis de nombreuses années.
Elles répondent parfaitement à mes besoins (gestion de thèmes personnalisés, manipulation de couleurs, etc.), et je pourrais difficilement m’en passer aujourd’hui.
J’espère, en tout cas, que cela vous incitera à les essayer dans vos futurs projets ! 🙂
Commentaires et discussions
Nos formations sur ce sujet
Notre expertise est aussi disponible sous forme de formations professionnelles !

CSS avancé
Mettre en forme des pages web en offrant des possibilités de personnalisation poussées
Ces clients ont profité de notre expertise
Dans le cadre du renouveau de sa stratégie digitale, Orpi France a fait appel à JoliCode afin de diriger la refonte du site Web orpi.com et l’intégration de nombreux nouveaux services. Pour effectuer cette migration, nous nous sommes appuyés sur une architecture en microservices à l’aide de PHP, Symfony, RabbitMQ, Elasticsearch et Docker.
L’équipe d’Alain Afflelou a choisi JoliCode comme référent technique pour le développement de son nouveau site internet. Ce site web-to-store incarne l’image premium de l’enseigne, met en valeur les collections et offre aux clients de nouvelles expériences et fonctionnalités telles que l’e-réservation, le store locator, le click & collect et l’essayage…
Nous avons entrepris une refonte complète du site, initialement développé sur Drupal, dans le but de le consolider et de jeter les bases d’un avenir solide en adoptant Symfony. La plateforme est hautement sophistiquée et propose une pléthore de fonctionnalités, telles que la gestion des abonnements avec Stripe et Paypal, une API pour l’application…