Vivement les variables CSS !

En février dernier, Aaron Gustafson expliquait sur son blog que les variables CSS sont une mauvaise idée. Ses principaux arguments sont que le support est pour l’instant mauvais et que la syntaxe est atroce. Et il n’a pas totalement tort. Les variables CSS ne fonctionnent pour le moment que sur Firefox. Et voici à quoi ressemble la déclaration et l’utilisation d’une variable en CSS.

body {
	--main-color: #333;
}

h1 {
	color: var(--main-color);
}

Si vous êtes habitué à la syntaxe habituelle des pré-processeurs, c’est sûr que la syntaxe à base de double tirets et la fonction var() sont plutôt rebutantes. Et pourtant, ces derniers mois, je suis tombé sur plusieurs intégrations qui m’ont fait dire à chaque fois : vivement les variables CSS !

La première chose à faire pour bien comprendre l’intérêt des variables CSS est de ne pas les appeler des variables CSS. Daniel Glazman l’expliquait il y a presque trois ans sur son blog (en précisant au passage pourquoi cette syntaxe a été retenue) :

La spécification proposée ne concerne pas des variables, et je me demande sérieusement si nous ne devrions pas changer le titre du document. Vous pouvez appeler « variables » les fonctionnalités qu’elle introduit, mais à un niveau plus profond, ce ne sont pas des variables. Plutôt des propriétés héritées définies par l’utilisateur. Ne prenez pas ça comme une précision politique ou une présentation masquant la réalité de la fonctionnalité. La spécification concerne vraiment le fait de vous laisser définir vos propres propriétés et d’atteindre leurs valeurs par cascade.

La dernière version de la spécification s’intitule ainsi très sobrement « Module de propriétés CSS personnalisées pour des variables en cascade de niveau 1 ». Il est très important d’avoir en tête cette notion de cascade lorsqu’on utilise des variables CSS. Et en voici un parfait exemple.

Des thèmes de couleur

C’est un problème d’intégration relativement courant : vous avez sur une même page différents composants (blocs, titres, liens, …) avec une couleur associée, déclinée selon le thème associé au composant. Le site du journal Le Monde illustre particulièrement bien cet exemple.

Différents thèmes de couleurs sur les rubriques de LeMonde.fr

Une couleur est associée à chaque rubrique du site. On retrouve cette couleur sur une bordure en haut de chaque rubrique, sur le titre de la rubrique, et en fond du sous-titre « Les blogs ».

Pour faire ça en CSS, on peut associer une classe ou un identifiant spécifique à chaque rubrique, puis redéfinir une règle pour la couleur de chaque élément qu’on souhaite modifier. En reprenant l’exemple ci-dessus, on obtiendrait le code suivant pour la première rubrique bleue :

.section--blue {
	border-top-color: #0386c3;
}

.section--blue .section__title {
	color: #0386c3;
}

.section--blue .aside__title {
	background-color: #0386c3;
}

Si l’on souhaite décliner pour la rubrique rouge, il faudra dupliquer la totalité de ce code, en y remplaçant la classe .section--blue par .section--red.

.section--red {
	border-top-color: #fe2f2f;
}

.section--red .section__title {
	color: #fe2f2f;
}

.section--red .aside__title {
	background-color: #fe2f2f;
}

À chaque fois qu’on voudra introduire un nouveau thème de couleur, on va devoir dupliquer tous ces styles. Rien que sur la page d’accueil du site du Monde, il y a plus de quinze thèmes de couleurs différents. Et plus on a d’éléments concernés par ce changement de couleur, plus on aura de sélecteurs dupliqués. Et là ça devient vite lourd et pénible à maintenir.

L’utilisation d’un pré-processeur peut faciliter les choses. On pourrait par exemple créer des listes des couleurs avec leurs codes hexadécimaux, et générer tous les sélecteurs nécessaires dans une boucle. Mais ça n’enlève rien au fait que le code CSS généré est peu élégant.

Voici maintenant comment transformer ça en utilisant des variables CSS. Pour chaque rubrique, on va définir une variable --theme-color correspondant à la couleur sélectionnée. On appellera ensuite cette variable avec la fonction var(--theme-color) là où il faut.

.section {
	border-top-color: var(--theme-color);
}

.section__title {
	color: var(--theme-color);
}

.aside__title {
	background-color: var(--theme-color);
}

Pour créer un thème de couleur, il faut écrire une règle similaire à la suivante définissant la variable --theme-color.

.section--blue {
	--theme-color: #0386c3;
}

Besoin de décliner pour la rubrique rouge ? Pas de problème.

.section--red {
	--theme-color: #fe2f2f;
}

Et c’est tout. Une déclinaison de tout un thème de couleur tient en une seule déclaration. La cascade CSS fait tout le reste du travail. Voici une démo complète illustrant tout ça sans variables CSS et avec variables CSS. Comparez le fichier CSS avec variables à celui sans variables, et admirez comme c’est de toute beauté.

Voici un autre exemple qui m’est venu à l’esprit.

Animations variables

Un chouette moyen de réaliser des animations un peu complexe sur le Web est d’utiliser des sprites. Mozilla le fait. Twitter le fait. Et Google aussi le fait, par exemple dans ce doodle de 2012.

Un sprite d'animation de Google

Avec un beau sprite tout prêt comme celui-ci, on va créer un élément HTML qui pourra l’accueillir en image de fond. On précisera ses dimensions en CSS de la manière suivante.

.sprite {
	width: 120px;
	height: 160px;
	background: url(sprite.png) no-repeat 0 0;
}

Ensuite, l’idée est de créer une animation CSS qui va décaler la position de l’image du sprite de toute sa longueur. Par exemple ici, avec notre sprite de 1440px de large, ça donne…

@keyframes animateBackground1440 {
	0% { background-position:0 0; }
	100% { background-position:-1440px 0; }
}

Enfin, il faut appliquer cette animation à notre élément. Grâce à une fonction de synchronisation d’animation steps(n), on précise le nombre d’étapes à notre animation, correspondant au nombre d’images inclus dans notre sprite. En 2012, Simurai expliquait brillamment l’utilisation de la fonction steps().

.sprite--doodle {
	animation: animateBackground1440 steps(12) 1s;
}

Le problème de cette technique, c’est que si on veut faire la même chose pour un autre sprite, d’une autre dimension, il faudra recréer une nouvelle animation. Ainsi, pour chaque nouveau sprite animé, il faudra réécrire le code suivant (par exemple ici pour un sprite de 500px de large composé de dix images).

@keyframes animateBackground500 {
	0% { background-position:0 0; }
	100% { background-position:-500px 0; }
}

.sprite--simurai {
	animation: animateBackground500 steps(10) 2s;
}

Faites ça quatre ou cinq fois sur une page, dupliquez le tout pour y ajouter les préfixes -webkit- encore nécessaires aujourd’hui, et vous avez un code CSS à rallonge particulièrement répétitif.

Grâce aux variables CSS, on va pouvoir créer une seule et unique animation utilisant une variable que j’ai appelé --background-offset.

@keyframes animateBackground {
	0% { background-position:0 0; }
	100% { background-position:var(--background-offset) 0; }
}

À chaque fois qu’on appellera cette animation, il faudra préciser la valeur souhaitée pour la variable --background-offset. Voici le code correspondant aux deux sprites utilisés précédemment.

.sprite--doodle {
	--background-offset: -3240px;
	animation: animateBackground steps(12) 1s;
}

.sprite--simurai {
	--background-offset: -500px;
	animation: animateBackground steps(10) 2s;
}

L’animation animateBackground créée reste unique et totalement réutilisable. Et ça, c’est beau. Voici une deuxième démo complète illustrant tout ça sans variables CSS et avec variables CSS. Encore une fois, comparez le fichier CSS avec variables à celui sans variables, et appréciez la clarté apportée par l’utilisation de variables CSS.

Conclusion

Les variables de pré-processeurs comme Sass permettent d’écrire du meilleur code Sass. Les « variables » CSS permettront d’écrire du meilleur code CSS. Elles n’ont pas pour but de supplanter les pré-processeurs. Je dirais même qu’utiliser les variables CSS dans cette optique serait une erreur. Mais bien utilisées, dans des cas bénéficiant de la cascade, les variables CSS permettront d’écrire un code CSS bien plus simple.

Dans les commentaires du premier article cité, Sara Soueidan indiquait un autre bon exemple d’utilisation de variables CSS pour modifier des images SVG. Et je suis convaincu que bien d’autres exemples d’utilisation pertinente fleuriront au fur et à mesure que le support navigateur s’étoffera.

Microsoft indique que l’implémentation des variables CSS est à l’étude. Blink (pour Chrome et Opera) est au point mort. On a donc encore au moins un ou deux ans avant de commencer à utiliser tout ça en production.

  1. David, le

    Très bon article qui montre bien l’utilité des variables CSS par rapport à celles des pré-processeurs.
    Une petite remarque sur le premier exemple. La valeur currentColor permettrait d’avoir juste à changer la couleur de texte du block pour que la bordure et le background-color en hérite automatiquement.

  2. Vincent De Oliveira, le

    Hello !

    Merci pour l’article. Je suis exactement du même avis que toi, et c’est un constat que j’ai fait lors de mes premiers tests de « variables » CSS : https://twitter.com/iamvdo/status/454566494945677312

    C’est aussi pour cela que je pense que les variables des préprocesseurs sont à privilégier à celles des postprocesseurs, qui n’apportent pas plus de fonctionnalités.

    Sinon, une petite info supplémentaire :
    – Le nouveau parser CSS dans Blink peut accélérer la prise en charge dans Chrome/Opera: https://twitter.com/simurai/status/581376869083131904

  3. LaboCss, le

    J’aime les variables, j’adore même.

    Mais pourquoi fucking bordel de quoi la syntaxe est :
    var(–mafuckingvariable)

    C’était pas bien $variable ?
    :/

  4. Rémi, le

    @David : La variable currentColor ne pourrait pas être utilisée pour le background, car le texte est redéfini en blanc. Pour le border par contre, ça peut fonctionner. Mais ça impliquerait de redéfinir la couleur des autres éléments, ce qui est un peu moins intuitif et peut être plus lourd dans le code.

    @Vincent : Merci pour l’info pour Blink, j’avais raté ça. Et très chouette exemple pour tes boutons.

    @LaboCss : L’article de Daniel Glazman cité dans mon article explique ça très bien. Le but est justement d’éviter la confusion avec de vraies variables.

  5. Loïs, le

    Oui effectivement, vivement les variables CSS…
    En attendant, on peut toujours utilisé rework (https://github.com/reworkcss/rework) à la place de SASS, LESS ou autre préprocesseur pour se préparer à les utiliser plus tard.