frontend / css
Variables CSS et thèmes
Explication
Ce que vous allez apprendre
- Centraliser des valeurs répétées (couleurs, espacements) dans des custom properties
- Comprendre pourquoi les variables CSS natives sont recalculées en temps réel, contrairement à Sass
- Implémenter un thème sombre en réassignant les mêmes noms de variables
- Fournir une valeur de repli avec
var(--x, valeur-par-defaut) - Redéfinir une variable localement à un composant, par exemple pour une variante compacte
Dans quel contexte ?
La direction artistique demande de remplacer le bleu principal de la marque, utilisé dans plus de 40 endroits différents du fichier theme.css (boutons, liens, bordures actives, icônes). Sans variable centralisée, il faudrait chercher-remplacer manuellement chaque occurrence de #2563eb, au risque d'en oublier certaines. En centralisant la couleur dans --couleur-primaire sur :root dès le départ du projet, un seul changement suffit à répercuter la nouvelle couleur partout où var(--couleur-primaire) est utilisé.
Étape 1 : le problème de la répétition
Imaginez devoir changer la couleur principale d'une marque, et devoir la retrouver dans des centaines d'endroits différents du CSS. C'est précisément le problème que résolvent les variables CSS.
Étape 2 : le principe, une valeur nommée une seule fois
L'idée est simple : définir une valeur UNE SEULE FOIS, lui donner un nom, puis la réutiliser partout via var(--nom). Changer la valeur à la source répercute automatiquement le changement partout où elle est utilisée.
Étape 3 : une vraie fonctionnalité du navigateur
Il existait déjà des variables avant, mais via des préprocesseurs comme Sass, qui les remplaçaient AVANT même d'envoyer le fichier au navigateur — un calcul figé une fois pour toutes.
| Type de variable | Calculée | Modifiable en JS/au clic |
|---|---|---|
Variable Sass ($couleur) | À la compilation, une fois | Non, il faut recompiler |
Variable CSS native (--couleur) | En temps réel, par le navigateur | Oui, immédiatement |
Étape 4 : ce qui change avec les variables natives
Les variables CSS natives sont différentes : elles sont évaluées EN TEMPS RÉEL par le navigateur, ce qui permet de les changer dynamiquement au clic ou en JavaScript, sans jamais recompiler quoi que ce soit.
Étape 5 : le thème sombre, cas d'usage emblématique
C'est ce mécanisme dynamique qui rend le thème sombre si simple à implémenter : on redéfinit les MÊMES noms de variables (--bg, --fg) avec des valeurs différentes selon un attribut comme [data-theme="dark"].
Étape 6 : pourquoi cette approche reste propre
Tout le reste du CSS continue d'utiliser var(--bg) sans jamais savoir quel thème est actif — une séparation nette entre "ce qui doit s'afficher" et "quelles couleurs utiliser en ce moment".
Un dernier point : la portée locale
Une variable n'est pas obligée d'être globale sur :root : elle peut être redéfinie localement dans un composant précis, par exemple .carte.compacte qui redéfinit --carte-padding, sans dupliquer toute la règle.
Réflexe à adopter
Toujours fournir une valeur de repli avec var(--couleur-primaire, #2563eb) : si la variable n'est pas encore définie (erreur de frappe, fichier pas encore chargé), le navigateur utilise la valeur de secours plutôt que d'afficher un style cassé.
Et ensuite ?
Une fois les variables maîtrisées, la prochaine étape consiste à donner du mouvement à l'interface avec les transitions et animations.
Commandes & code
Variables CSS et thèmes
Des custom properties calculées à l'exécution par le navigateur — pas une préprocessing statique.
:root {
/* Design tokens centralisés */
--couleur-primaire: #2563eb;
--couleur-texte: #1a1a1a;
--couleur-fond: #ffffff;
--espacement-base: 8px;
--rayon-bordure: 8px;
--ombre-carte: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.bouton {
background: var(--couleur-primaire);
border-radius: var(--rayon-bordure);
padding: calc(var(--espacement-base) * 2) calc(var(--espacement-base) * 3);
}
/* valeur de repli si la variable n'existe pas */
.texte {
color: var(--couleur-texte-secondaire, #666);
}/* Thème sombre : réassigner les MÊMES variables dans un contexte différent */
:root {
--bg: #ffffff;
--fg: #1a1a1a;
--bordure: #e5e5e5;
}
[data-theme="dark"] {
--bg: #0f0f0f;
--fg: #f0f0f0;
--bordure: #2a2a2a;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--bg: #0f0f0f;
--fg: #f0f0f0;
}
}
body {
background: var(--bg);
color: var(--fg);
transition: background 0.2s, color 0.2s;
}
/* Tout le reste du CSS ne change JAMAIS : il consomme les variables,
sans jamais connaître le thème actif *//* Variables locales à un composant : portée limitée, réassignables par contexte */
.carte {
--carte-padding: 16px;
padding: var(--carte-padding);
}
.carte.compacte {
--carte-padding: 8px; /* même règle .carte, juste la variable qui change */
}// Variables ET JavaScript : lecture/écriture dynamique, contrairement à Sass/Less
const racine = document.documentElement;
// lecture
const primaire = getComputedStyle(racine).getPropertyValue("--couleur-primaire").trim();
// écriture -> répercutée immédiatement partout où la variable est utilisée
racine.style.setProperty("--couleur-primaire", "#16a34a");
// bascule de thème typique
document.querySelector("#toggle-theme").addEventListener("click", () => {
const actuel = racine.dataset.theme === "dark" ? "light" : "dark";
racine.dataset.theme = actuel;
localStorage.setItem("theme", actuel);
});Résumé
- Les custom properties sont calculées à l'exécution : lisibles/modifiables en JavaScript, contrairement aux variables Sass.
- Un thème sombre se résume à réassigner les mêmes noms de variables dans un sélecteur différent.
- Une variable déclarée localement à un composant peut être surchargée par une classe modificatrice.
var(--x, repli)fournit une valeur de secours propre si la variable n'est pas définie.
Exercices pratiques
Mission : un thème sombre à moitié cassé sur une variable oubliée
Objectif : Diagnostiquer une variable non redéfinie dans un thème et comprendre le calcul en temps réel des custom properties.
Contexte
Sur theme.css, :root définit --bg: #ffffff;, --fg: #1a1a1a; et --bordure: #e5e5e5;. Le sélecteur [data-theme="dark"] redéfinit bien --bg: #0f0f0f; et --fg: #f0f0f0;, mais un développeur a oublié d'y redéfinir --bordure. Résultat : en thème sombre, toutes les bordures de carte restent d'un gris très clair (#e5e5e5), presque invisibles sur le fond sombre.