Chapitre 12 : Fondamentaux des Custom Properties (Variables CSS)
Déclaration et héritage des variables CSS via --var et var()
Les Custom Properties
1. Quoi
Les Custom Properties, plus communément appelées Variables CSS, sont des entités définies par le développeur qui permettent de stocker une valeur spécifique (couleur, taille, police, etc.) pour la réutiliser tout au long d'une feuille de style.
Contrairement aux variables dans les préprocesseurs comme Sass ou Less (qui sont compilées et disparaissent une fois le CSS généré), les Custom Properties sont natives au navigateur. Elles sont dynamiques, respectent la cascade CSS et peuvent être modifiées en temps réel via JavaScript.
2. Pourquoi
Dans un projet professionnel, la maintenance d'un site web peut devenir un cauchemar si une couleur spécifique est répétée 150 fois dans un fichier CSS de 3000 lignes. Si le client décide de changer le "Bleu Corporate" par un "Bleu Nuit", modifier chaque occurrence manuellement est source d'erreurs et chronophage.
L'utilisation des variables CSS permet de :
- Centraliser la configuration : On définit la valeur à un seul endroit.
- Faciliter le Theming : Créer un mode sombre (Dark Mode) devient trivial en changeant simplement la valeur des variables.
- Améliorer la lisibilité :
--primary-colorest beaucoup plus explicite que#3498db. - Interagir avec le JS : Le JavaScript peut lire ou modifier une variable CSS, permettant des animations ou des personnalisations d'interface dynamiques.
3. Comment
A. Syntaxe de base
Une variable CSS se compose de deux étapes : la déclaration et l'utilisation.
- Déclaration : Elle commence obligatoirement par deux tirets (
--). - Utilisation : On utilise la fonction
var()pour appeler la valeur.
/* Déclaration */
:root {
--main-bg-color: #f0f0f0;
}
/* Utilisation */
body {
background-color: var(--main-bg-color);
}
B. Cas concret : Système de Design Tokens
En production, on ne déclare pas les variables au hasard. On utilise souvent le concept de Design Tokens. On sépare les variables "primitives" (les valeurs brutes) des variables "sémantiques" (ce que la valeur représente).
:root {
/* 1. Primitives : Valeurs brutes, pas de sens métier */
--color-blue-500: #3b82f6;
--color-blue-700: #1d4ed8;
--color-gray-100: #f3f4f6;
--color-gray-900: #111827;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
/* 2. Sémantique : On lie la primitive à un usage */
--brand-primary: var(--color-blue-500);
--brand-primary-hover: var(--color-blue-700);
--bg-app: var(--color-gray-100);
--text-main: var(--color-gray-900);
--container-padding: var(--spacing-md);
}
/* Application concrète */
.card {
background-color: var(--bg-app);
color: var(--text-main);
padding: var(--container-padding);
border: 1px solid var(--brand-primary);
}
.card button {
background-color: var(--brand-primary);
color: white;
padding: var(--spacing-sm) var(--spacing-md);
}
.card button:hover {
background-color: var(--brand-primary-hover);
}
C. Limitations et spécificités
- La Cascade et l'Héritage : Les variables CSS héritent des propriétés de leurs parents. Si vous définissez une variable dans
:root, elle est disponible partout. Si vous la redéfinissez dans une classe.card, elle ne sera valable que pour.cardet ses enfants. - Valeurs non calculées : Vous ne pouvez pas utiliser
var()pour construire une valeur complexe sans la fonctioncalc().- ❌
margin: var(--spacing) * 2;(Ne fonctionne pas) - ✅
margin: calc(var(--spacing) * 2);
- ❌
- Fallback (Valeur de secours) : La fonction
var()accepte un deuxième argument qui sera utilisé si la variable n'est pas définie.color: var(--main-color, black);(Utilise noir si--main-colorest absent).
4. Zone de Danger
❌ Erreur commune : Déclarer des variables dans chaque sélecteur
Déclarer --color: red; à l'intérieur de .btn puis --color: blue; dans .btn-large peut rendre le débogage difficile si vous ne maîtrisez pas la cascade.
✅ Bonne pratique : Utiliser :root pour le global
Le pseudo-sélecteur :root représente l'élément le plus haut de la hiérarchie (généralement <html>). C'est l'endroit idéal pour stocker vos variables globales afin qu'elles soient accessibles partout dans le DOM.
Cycle de résolution des Custom Properties
Questions clés
Quelle est la différence majeure entre une variable Sass ($color) et une variable CSS (--color) ?
Découvrir la réponse
La variable Sass est traitée lors de la compilation : elle est remplacée par sa valeur finale dans le fichier CSS envoyé au navigateur. La variable CSS est traitée par le navigateur en temps réel : elle peut être modifiée dynamiquement (via JS ou Media Queries) et respecte la cascade du DOM.
Pourquoi utiliser :root pour déclarer les variables ?
Découvrir la réponse
:root cible l'élément racine du document. En déclarant les variables ici, on s'assure qu'elles sont héritées par tous les autres éléments du document, créant ainsi un magasin de configuration global.
Que se passe-t-il si j'utilise var(--ma-variable) alors que celle-ci n'a jamais été déclarée ?
Découvrir la réponse
Le navigateur ignorera la propriété CSS concernée (elle sera considérée comme invalide), à moins qu'une valeur de secours (fallback) n'ait été fournie comme deuxième argument : var(--ma-variable, fallback-value).
Peut-on modifier une variable CSS avec JavaScript ?
Découvrir la réponse
Oui, absolument. On utilise la méthode setProperty sur l'élément souhaité.
Exemple : document.documentElement.style.setProperty('--main-color', 'green'); pour modifier la variable globale.
Mise en pratique
Exercice 1 : Reproduction guidée
Objectif : Créer un système de couleurs basique.
- Déclarez trois variables dans
:root:--primary-color(bleu),--secondary-color(gris) et--text-color(noir). - Créez un élément
divavec la classe.box. - Appliquez la couleur primaire au fond de la box, la couleur secondaire à la bordure et la couleur de texte au contenu.
Découvrir la solution commentée
:root {
/* Définition des tokens de base */
--primary-color: #007bff;
--secondary-color: #6c757d;
--text-color: #212529;
}
.box {
/* Utilisation des variables via var() */
background-color: var(--primary-color);
border: 2px solid var(--secondary-color);
color: var(--text-color);
padding: 20px;
text-align: center;
}
Exercice 2 : Adaptation (Le Mode Sombre)
Objectif : Utiliser la redéfinition de variables pour créer un thème.
- Reprenez le code de l'exercice 1.
- Créez une classe
.dark-themeque vous appliquerez aubody. - À l'intérieur de
.dark-theme, redéfinissez les variables--primary-color,--secondary-coloret--text-coloravec des couleurs adaptées à un mode sombre (ex: fond sombre, texte clair).
Découvrir la solution commentée
:root {
--primary-color: #007bff;
--secondary-color: #6c757d;
--text-color: #212529;
}
/* On redéfinit les valeurs des variables pour tout le scope du body.dark-theme */
body.dark-theme {
--primary-color: #0056b3;
--secondary-color: #495057;
--text-color: #f8f9fa;
/* On change aussi le fond du body pour que le contraste soit visible */
background-color: #121212;
}
.box {
background-color: var(--primary-color);
border: 2px solid var(--secondary-color);
color: var(--text-color);
padding: 20px;
}
Exercice 3 : Conception (Calculs dynamiques)
Objectif : Créer un système de grille flexible utilisant calc() et les variables.
- Déclarez une variable
--base-unit: 8px;dans:root. - Créez une carte
.carddont :- Le
paddingest égal à 2 fois la base unit. - La
margin-bottomest égale à 3 fois la base unit. - La
border-radiusest égale à 1 fois la base unit. - Le fond (
background) est blanc. - Une ombre portée (
box-shadow) légère est appliquée. - Une bordure (
border) grise fine encadre la carte.
- Le
- Vérifiez que si vous changez
--base-unità12px, toute la mise en page de la carte s'adapte proportionnellement.
Découvrir la solution commentée
:root {
/* Unité de base pour tout le design system */
--base-unit: 8px;
}
.card {
/* On utilise calc() pour multiplier la variable par un facteur */
padding: calc(var(--base-unit) * 2);
margin-bottom: calc(var(--base-unit) * 3);
border-radius: var(--base-unit);
background: white;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
border: 1px solid #ddd;
}
/*
Si on change --base-unit en 12px,
le padding devient 24px et la margin 36px automatiquement.
*/