CSS Variables : Le guide complet
Des variables ? En CSS ?
Oui, oui. En CSS, vous avez bien lu. Et c’est possible depuis un moment.
Le W3C travaille sur ce module CSS depuis 2012 déjà et la spécification actuellement implémentée par les navigateurs date de décembre 2015.
Aujourd’hui, le support navigateur est plutôt bon (env. 90% du marché global). Si vous ne cherchez pas de compatibilité avec Internet Explorer, c’est même excellent.
A travers cet article nous allons :
- Apprendre à créer des variables CSS
- Créer des thèmes avec les variables CSS
- Créer des transitions et des animations avec les variables CSS
- Gérer des Media Queries avec les variables CSS
- Comparer les préprocesseurs CSS et les variables CSS
- Apprendre à accéder aux variables CSS avec JavaScript
- Évoquer les limites des variables CSS
Comment ça marche ?
Déclarer et utiliser une variable
body { --text-color: #333; }
Une variable se déclare comme une propriété CSS classique mais avec --
en préfixe. On parle en réalité de CSS Custom property. La syntaxe peut faire penser aux propriétés avec des vendor prefixes comme -webkit-*
. Un peu déconcertant certes, mais on s’y fait rapidement.
Une variable doit être déclarée pour un scope donné → Un sélecteur CSS (body
dans notre cas).
Utilisons maintenant notre variable :
body { --text-color: #333; color: var(--text-color); }
La fonction var()
permet de faire référence à notre variable. La notion de référence est importante car c’est ce qui permet l’aspect dynamique. Ainsi, il est possible de faire référence à une variable qui sera déclarée plus tard :
body { color: var(--text-color); } /* Plusieurs lignes après */ body { --text-color: #333; }
Attention : Le nom des variables est sensible à la casse !
Valeur par défaut
Il est possible de fixer des valeurs par défaut aux références. En effet la fonction var()
accepte un paramètre supplémentaire pour faire un fallback au cas où une variable ne serait pas définie.
body { --text-color: #333; color: var(--textColor, #ddd); }
Ici, nous faisons référence à une variable --textColor
qui n’existe pas. Le second paramètre de var()
va donc être utilisé comme valeur de substitution.
Héritage
Comme les propriétés classiques, les CSS custom properties tiennent également compte de l’héritage et de l’imbrication des éléments : La cascade. Si nous souhaitons déclarer une variable globale et donc utilisable par tous les éléments de la page, nous allons utiliser le sélecteur :root
(élément racine de la page) :
:root { --text-color: #333; } body { color: var(--text-color); }
Surcharge
Habituellement, nous devons faire attention aux sélecteurs CSS que nous écrivons pour ne pas qu’ils prennent trop de “poids” et qu’ils soient trop spécifiques. D’une part pour des raisons de performance et de l’autre pour garder un code modulaire et flexible.
Prenons l’exemple du code HTML suivant :
<h2>Un titre</h2> <div id="main"> <h2>Un autre titre</h2> </div>
Ainsi que ce code CSS :
h2 { color: #0f3057; } #main { color: #333; }
Nous obtenons le résultat suivant :
Si nous souhaitons que tous les <h2>
héritent de la couleur de <div id="main">
nous devons écrire une règle CSS supplémentaire et plus spécifique :
h2 { color: #0f3057; } #main { color: #333; } #main h2 { color: inherit; }
Les variables CSS permettent de régler ce problème assez simplement :
h2 { color: var(--title-color, #0f3057); } #main { --text-color: #333; --title-color: var(--text-color); color: var(--text-color); }
Nous n’avons pas eu besoin de créer de règle supplémentaire. Notre h2
fait référence à une variable qui sera définie dans #main
. Nous nous servons également du paramètre fallback pour définir une valeur par défaut pour la couleur des h2.
Thèmes
Etant donné que les variables CSS fonctionnent par références, il est possible de créer facilement des thèmes.
Thème d’un élément
Nous souhaitons créer un style de bouton avec des variantes de couleurs et de tailles pour obtenir le rendu suivant :
Nous allons d’abord définir un bouton avec des styles de base (avec des fallbacks) ainsi que des références vers des variables qui seront utilisées plus tard :
.btn { border-radius: var(--btn-corner, 3px); background: var(--btn-bg, #194769); color: var(--btn-color, #fff); padding: var(--btn-padding, 10px); }
Nous devons maintenant créer 3 classes qui ne modifieront que les variables nécessaires :
.btn-success { --btn-bg: #0e9577; } .btn-danger { --btn-bg: #a40a3c; } .btn-lg { --btn-corner: 6px; --btn-padding: 20px; }
Thème de plusieurs éléments
Par extension, nous pouvons imaginer faire des thèmes à l’échelle d’un site entier.
Supposons que nous construisons un site avec 3 rubriques possédant chacune une couleur spécifique :
- Vert pour la rubrique “Sport”
- Bleu pour la rubrique “Programmation”
- Orange pour la rubrique “Culture”
Nous avons 3 blocs contenant 3 éléments dans notre code HTML (très simplifié) :
<div class="cat-sport"> <h2>Sport</h2> <blockquote>Lorem ipsum dolor sit ...</blockquote> <button class="btn">Bouton</button> </div> <div class="cat-programming"> <h2>Sport</h2> <blockquote>Lorem ipsum dolor sit ...</blockquote> <button class="btn">Bouton</button> </div> <div class="cat-culture"> <h2>Sport</h2> <blockquote>Lorem ipsum dolor sit ...</blockquote> <button class="btn">Bouton</button> </div>
Comme il n’y a qu’une couleur par rubrique, nous devons créer des styles faisant référence à une seule variable :
h2 { ... color: var(--theme-color); } blockquote { ... border-left: 5px solid var(--theme-color); } .btn { ... background: var(--theme-color); }
Enfin, nous utilisons la classe de la rubrique pour définir notre variable :
.cat-sport { --theme-color: #4A772F; } .cat-programming { --theme-color: #005E7C; } .cat-culture { --theme-color: #F5841A; }
Transitions et animations
Transitions CSS
Les transitions peuvent être utilisées de manière habituelle avec la propriété transition
. Le navigateur est assez intelligent pour faire transiter une variable d’un état A à un état B.
div { background: var(--bg, #1989AC); transition: .5s; } div:hover { --bg: #283E56; }
Animation CSS
L’utilisation des variables CSS est intéressante pour les animations car elles permettent d’agir sur ces dernières comme des paramètres.
Prenons en exemple les 2 blocs suivants qui se basent sur la même animation :
Nous ne devons créer qu’une seule animation mais fournir 2 variables pour, respectivement, la couleur de début et celle de fin :
div { ... animation: bg-animation 1s infinite alternate; } .block-1 { --bg-1: #1989AC; --bg-2: #283E56; } .block-2 { --bg-1: #F12B6B; --bg-2: #970747; } @keyframes bg-animation { from { background: var(--bg-1); } to { background: var(--bg-2); } }
Media Queries
Il est également possible d’utiliser des variables CSS au sein des media queries.
body { background: var(--bg, #F9A828); } @media screen and (min-width: 768px) { :root { --bg: #1989AC; } }
Quels avantages par rapport à SASS / LESS ?
L’utilisation des préprocesseurs CSS comme SASS ou LESS, est rentrée dans les habitudes des Intégrateurs / Développeurs Web. Qui se souvient de son dernier projet sans préprocesseur ?
Mais alors pourquoi utiliser les variables CSS alors que les préprocesseurs nous permettent déjà d’en créer et apportent encore plus de possibilités (mixins, fonctions, boucles, conditions, …) ?
La réponse est que les variables CSS sont vraiment dynamiques et modifiables après le chargement de la page. Les préprocesseurs permettent certes de faire des styles dynamiques mais uniquement avant la compilation, le fichier CSS résultant reste statique au final.
Variables CSS vs. SASS
L’autre différence majeure réside dans le code généré : Les variables CSS ne génèrent pas plus de code que ce nous écrivons.
Reprenons l’exemple du theming par rubrique vu plus haut mais avec du SASS. Pour arriver au même résultat, nous devons d’abord créer un tableau de correspondance entre la rubrique et sa couleur :
$categories: ( cat-sport: #4A772F, cat-programming: #005E7C, cat-culture: #F5841A );
Enfin, nous devons boucler sur ce tableau pour créer toutes les règles spécifiques aux catégories :
@each $category, $color in $categories { .#{$category} { h2 { color: $color; } blockquote { border-left-color: $color; } .btn { background: $color; } } }
Voici le code généré :
.cat-sport h2 { color: #4A772F; } .cat-sport blockquote { border-left-color: #4A772F; } .cat-sport .btn { background: #4A772F; } .cat-programming h2 { color: #005E7C; } .cat-programming blockquote { border-left-color: #005E7C; } .cat-programming .btn { background: #005E7C; } .cat-culture h2 { color: #F5841A; } .cat-culture blockquote { border-left-color: #F5841A; } .cat-culture .btn { background: #F5841A; }
Résultat : Nous avons écrit 3 fois plus de code avec SASS et nous nous trouvons maintenant avec 9 règles CSS.
Variables CSS & SASS
En réalité, il y a des choses que les variables CSS peuvent faire que les préprocesseurs ne peuvent pas, et inversement. Du coup, il n’y a aucun mal à tirer profit des 2 mondes en combinant un préprocesseur et des variables CSS.
Nous pouvons très bien garder le tableau de couleurs et la boucle SASS pour générer les règles CSS et les variables natives :
@each $category, $color in $categories { .#{$category} { --theme-color: #{$color}; } }
Accéder aux Variables CSS avec JavaScript
Il est possible de récupérer et de modifier une variable CSS avec du JS assez facilement.
Récupération
La récupération de la valeur d’une variable CSS, se fait en utilisant la fonction getPropertyValue()
sur les styles d’un élément HTML :
let body = document.querySelector('body'); let bgColor = getComputedStyle(body).getPropertyValue('--body-bg');
Modification
Comme évoqué depuis le début, les variables CSS restent modifiables après le chargement de la page. Les références sont toujours présentes et “écoutent” les changements sur les variables. Une mise à jour de la valeur d’une variable en JS sera donc automatiquement impactée sur le code CSS.
La modification d’une variable se fait en utilisant la fonction setProperty()
sur les styles d’un élément HTML :
let body = document.querySelector('body'); body.style.setProperty('--body-bg', '#7BCECC');
Une mise à jour de variable génère une style inline :
<body style="--body-bg: #7BCECC;"> ... </body>
Limitations
Comme nous ne sommes pas dans un monde parfait (ce sont des CSS après tout), il existe quelques limitations à l’utilisation des variables CSS. Certaines sont dûes à la spécification, d'autres à un mauvais support des navigateurs. Voici les principales limitations :
Chemins vers des fichiers
Le chemin vers un fichier ne peut pas être morcelé pour être utilisé dans une variable. Il doit même être défini avec url()
pour fonctionner.
/* Non valide */ body { --body-bg-url: 'loremflickr.com'; background: url(https://var(--body-bg-url)/600/600) 50% 50% no-repeat; } /* Non valide */ body { --body-bg-url: 'https://loremflickr.com/600/600'; background: url(var(--body-bg-url)) 50% 50% no-repeat; } /* Valide */ body { --body-bg-url: url(https://loremflickr.com/600/600); background: var(--body-bg-url) 50% 50% no-repeat; }
Unités
Il n’est pas possible de définir une variable numérique et de lui attacher une unité lorsque nous l’utilisons. Pour cela, il faut soit définir l’unité dans la variable, soit utiliser calc()
avec un multiplicateur.
/* Non valide */ body { --font-size: 14; background: var(--font-size)px; } /* Valide */ body { --font-size: 14; font-size: calc(var(--font-size) * 1px); }
Sélecteurs et propriétés dynamiques
Il n’est pas possible de créer des variables pour rendre des sélecteurs ou des propriétés dynamiques.
:root { --selector: body; } /* Non valide */ var(--selector) { font-size: 14px; } /* Non valide */ body { --prop: font-size; var(--prop): 14px; }
Media queries
Nous avons vu plus haut qu’il est possible de définir des variables dans des media queries. Par contre, la cible d’une media query ne peut pas être variabilisée.
:root { --mq: 768px; --screen-size: 768px; } /* Non valide */ @media (min-width: var(--screen-size)) { ... }
Valeurs vides
Il n’est pas possible de définir une variable sans valeur. Cependant un espace est une valeur. La différence est subtile mais potentiellement source de problèmes :
:root { --invalid:; /* Non valide */ --valid: ; /* Valide */ }
Valeurs incompatibles
Lorsqu’une variable est écrasée avec une valeur incompatible avec une propriété, elle ne reprend pas la dernière valeur correcte définie. La valeur devient en réalité initial
.
body { --bg-color: #f00; --bg-color: 14; background: var(--bg-color); /* Devient "initial" et pas #f00 */ }
Une chose étonnante se produit lorsque nous utilisons une valeur incompatible avec un fallback :
body { --bg-color: 14; background: var(--bg-color, #f00); }
Nous nous attendons à ce que la valeur en fallback soit appliquée. Or, ce dernier n’est censé fonctionner que si la variable n’est pas définie. Comme vu précédemment, la valeur incompatible est considérée comme initial
et non undefined
. Ici, le background
devient transparent
.
Chat de Schrödinger
Le fait que var()
soit une référence dynamique, écraser une variable avec sa propre référence n’est pas possible. Ceci est dû au fait que CSS est “déclaratif” et que l’ordre de définition n’est pas important. Il n’y a pas d’état avant ou après une valeur → Le calcul est impossible.
div { --height: 200px; height: var(--height); } div:nth-child(2) { --height: calc(var(--height) * 2); }
Une propriété ne peut avoir qu’une seule valeur à un moment donné et pour un élément donné.
Dans cet exemple, la hauteur de la 2ème <div>
sera initial
et non 400px
.
Conclusion
Les CSS variables (ou CSS Custom Properties) propulsent le langages CSS dans une toute autre dimension. Elles permettent d'obtenir des styles réellement dynamiques tout en réduisant le code produit : les avantages sont énormes ! De plus, la possibilité de combiner les variables CSS avec un préprocesseur rend leur utilisation encore plus intéressante.
Même s'il existe encore quelques limitations (dont certaines qui seront réglées dans le futur), les CSS variables ne sont plus une utopie. Encore une fois, si votre projet le permet, foncez !
Quelques ressources pour aller plus loin
- Myth.io - CSS the way it was imagined
- CssNext.io - Use tomorrow’s CSS syntax, today
- Css-vars: Use CSS Custom Properties with Sass
- CSS4 Variables and Sass
❤ Made with CSS & love.
Commentaires