Article écrit par Victor Darras
Bonjour à tous, aujourd’hui j’aimerais expérimenter avec vous les possibilités offertes par les variables CSS disponibles depuis maintenant plusieurs mois sur la majorité des navigateurs modernes. Nous verrons ensemble les capacités ou limites de l’outil en créant un thème dynamique sur base d’une simple couleur.
Les variables CSS ?
Faisons ensemble un rapide tour d’horizon de cette fonctionnalité. Tout d’abord, voyons un peu comment on écrit et utilise une variable CSS, aussi appelée custom properties.
Pour la syntaxe, c’est assez déroutant mais il est nécessaire de préfixer le nom d’une variable par --
, notamment pour la distinguer d’un nom de propriété classique ou d’une propriété elle-même préfixée d’un -moz-
ou d’un -webkit-
. Pour en récupérer la valeur nous utiliserons la fonction var()
.
Ce qui nous donnera à l’usage quelque chose dans ce genre :
:root { --primary-color: #B4D455; } .el { color: var(--primary-color); }
:root ? Mais qu’est-ce ?
Découvert, entre autres, avec l’apparition des variables, cet élément désigne la racine du document, et est donc équivalent à une définition de variable globale.
Quelle différence avec les variables SASS/LESS/PostCSS ?
Si vous écrivez des CSS depuis plus de quelques mois, vous avez sans nul doute déjà eu l’occasion de compiler vos feuilles de style avec SASS ou PostCSS afin de profiter de variables.
Une grosse distinction se doit d’être faite entre ces 2 types de variables. Dans le cas des pré/post-procésseurs, la compilation « fige » les valeurs des variables pour chaque propriété où elles sont utilisées. Vous n’aurez donc aucune possibilité de redéfinir la valeur d’une variable au runtime, autrement dit dans le navigateur.
À l’inverse, les variables CSS nous permettent de modifier leurs valeurs dynamiquement en fonction de certains sélecteurs ou scopes particulier sans avoir à redéfinir les propriétés concernées :
.el { color: var(--primary-color); } .el:hover { --primary-color: #B4D466; }
Ou même en JavaScript !
root.style.setProperty('--primary-color', 'tomato');
Il existe une multitude d’usages différents de cette nouvelle fonctionnalité, mais plutôt qu’essayer d’en faire une liste exhaustive (spoiler, c’est chaud) je vous propose un petit exercice de style.
Et du coup, à quoi ça sert ?
Dans votre vie de tous les jours, c’est un remplacement idéal pour les cas où vous utilisez des variables SASS ou LESS, puisque ça vous permet plus de souplesse, voir de vous passer du préprocesseur si — comme moi — vous vous en serviez quasi exclusivement à cette fin.
J’ai, pour ma part plutôt en tête des possibilités de theming assez avancées et c’est ce que nous allons voir avec un petit exemple ici. Pour l’exercice, je vous propose de créer un fichier HTML simple, contenant quelques éléments qui nous serviront d’exemple pour appliquer un thème dynamique, notamment un input qui nous permettra de modifier la couleur primaire du thème.
<body> <h1>Welcome to the CSS <span class="color">color</span> experiment</h1> <p>You can change this page, by modifying the global color with the following input.</p> <input type="color" oninput="updateCSS(this)"> <hr> <button class="primary">Check</button> <button class="secondary">Check</button> <button class="tertiary">Check</button> </body>
En l’état, et pour gagner du temps, nous allons ajouter un poil de JavaScript qui répondra à l’appel d’updateCSS()
présent à l’événement de changement de valeur de l’input.
var updateCSS = function(el) { let root = document.documentElement; let color = hexToHSL(el.value); root.style.setProperty('--primary-color-hue', color.h); root.style.setProperty('--primary-color-sat', color.s + "%"); root.style.setProperty('--primary-color-lit', color.l + "%"); }
L’idée ici est très simple, récupérer la valeur entrée dans l’input par l’utilisateur, la transformer en HSL, puis définir des variables globales à notre document.
2 mots sur HSL
Pour cet exercice assez particulier, j’ai l’intention de jouer avec les couleurs de manière relativement avancée. Je vais donc préférer aux syntaxes hexadécimales ou RGBA historiques, une syntaxe HSL plus moderne et souple. Le H
pour la teinte (hue), le S
pour la saturation et le L
pour la luminosité. Une couche d’alpha est disponible comme avec RGBA avec la syntaxe HSLA()
mais nous ne l’utiliserons pas aujourd’hui.
Le code de transformation de la syntaxe RGB vers HSL est relativement complexe et n’est absolument pas l’objet de cet article, mais je vous invite à découvrir celui qui m’a servi pour cet exercice et est très bien expliqué à cette adresse.
Here come the sun
Nous entrons enfin dans le vif du sujet, et nous allons écrire du CSS ! L’idée sera donc de laisser l’utilisateur choisir une couleur primaire, de laquelle nous afficherons un thème, ou une déclinaison de couleurs pour plusieurs éléments de la page simultanément. Comme vu précédemment, commençons par définir un ensemble de variables pour chaque attribut HSL, ainsi qu’une variable qui les combine pour se faciliter la vie.
:root { --color-hue: 180; --color-sat: 50%; --color-lit: 50%; --color-primary: hsl( var(--color-hue), var(--color-sat), var(--color-lit) ); }
Nous allons définir une couleur de fond sur le body
afin de donner une teinte globale au site. L’idée est de récupérer la teinte et sa saturation tout en ajoutant une luminosité quasiment au maximum. Nous allons aussi définir une classe simple qui nous permettra de colorer du texte au besoin.
body { background: hsl( var(--color-hue), var(--color-sat), 98% ); } .colored { color: var(--color-primary); }
Et voici un ensemble de styles classique pour un bouton, dont nous rendons le fond et la bordure dynamique. Le fond utilise notre helper (en réalité, juste une variable supplémentaire) qui combine toutes les autres. La bordure quant à elle utilise la couleur d’origine, avec une luminosité diminuée de 10%.
button { appearance: none; padding: 0.5em 1em; border-radius: 10px; font-size: 1rem; color: #fff; background: var(--color-primary); text-shadow: 0 2px 3px #00000033; border: 2px solid; border-color: hsl( var(--color-hue), var(--color-sat), calc(var(--color-lit) - 10%) ); }
Vous devriez déjà pouvoir modifier les couleurs de la page en modifiant la couleur avec l’input. Maintenant, nous allons définir les styles pour les 2 autres boutons : secondary et tertiary.
Pour l’exercice, nous allons simplifier un peu la sélection des couleurs et partir du principe que les 2 autres couleurs disponibles de notre page sont respectivement à 120 et 240 degrés sur un cercle chromatique.
button.secondary { background: hsl( calc(120 + var(--color-hue)), var(--color-sat), var(--color-lit) ); border-color: hsl( calc(120 + var(--color-hue)), var(--color-sat), calc(var(--color-lit) - 10%) ); }
Ici nous redéfinissons donc les propriétés background
et border
en modifiant la teinte de chacune des couleurs avec calc()
. Mais je ne suis pas satisfait, je voudrais trouver une solution qui me permette de redéfinir les styles en 1 seule ligne de CSS. Puisque je ne peux pas réécrire une variable sur base d’elle-même :
--color-hue: calc(var(--color-hue) + 120); /* Ne fonctionne pas */
Il va me falloir une seconde variable pour le modifier que nous utiliserons dans la définition du background et que nous écraserons pour chacun des boutons.
Si je reprends, on définit une valeur de modifier dans le root
, on remplace la teinte du bouton d’origine, et on écrase la valeur de --color-mod
pour nos deux boutons alternatifs :
:root { /* ... */ --color-mod: 0; } button { background: hsl( calc(var(--color-hue) + var(--color-mod)), var(--color-sat), var(--color-lit) ); border-color: hsl( calc(var(--color-hue) + var(--color-mod)), var(--color-sat), calc(var(--color-lit) - 10%) ); } button.secondary { --color-mod: 120; } button.tertiary { --color-mod: 240; }
Une image vaut mille mots, mais un CodePen alors ?
See the Pen CSS variables experiment by Victor Darras (@victordarras) on CodePen.
J’espère que cet exercice vous aura plu et permis d’en apprendre un peu plus sur l’usage des custom properties. Je vous invite tout de même à jeter un œil à la documentation qui passera en revue tous les éléments que j’ai omis.