Niveau 23 — Variables CSS

Les Variables CSS

Les Custom Properties transforment le CSS en un vrai langage de programmation. Gérez vos thèmes, créez des design systems et dynamisez vos styles avec JavaScript.

1. Comprendre les variables CSS

En une phrase : Les Variables CSS (Custom Properties) permettent de stocker des valeurs réutilisables directement dans CSS, avec un support de la portée (scope), des valeurs par défaut et de la modification dynamique via JavaScript.

Les variables CSS sont au CSS ce que les variables sont à tout langage de programmation. Elles permettent de DRY (Don't Repeat Yourself) vos feuilles de style, de créer des design systems cohérents, et de rendre le CSS dynamique.

VARIABLES CSS vs PRÉPROCESSEURS (Sass, Less)
═══════════════════════════════════════════════

  SASS ($variable)                    CSS (Custom Properties)
  ─────────────────                   ───────────────────────
  • Compilées à la build              • natives du navigateur
  • Pas de portée dynamique           • Scope réel (inheritance)
  • Pas de modification JS            • Modifiables via JS
  • Pas de media queries              • Utilisables dans media queries
  • Fonctionnent en build only        • Fonctionnent en runtime

  $couleur: #2563eb;                  :root { --couleur: #2563eb; }
  .el { color: $couleur; }           .el { color: var(--couleur); }

  ❌ Impossible en Sass :             ✅ Possible en CSS :
  $couleur = getComputedStyle()       var(--couleur) + JavaScript

Avantages des variables CSS native par rapport aux préprocesseurs

Pourquoi les variables CSS sont essentielles

Besoin Sans variables Avec variables
Changement de couleur Modifier 50+ lignes manuellement Changer 1 variable, tout se met à jour
Thème dark mode Dupliquer tous les styles Redéfinir les variables, même HTML
Design system Valeurs magiques partout Tokens nommés et réutilisables
Espacement cohérent 4px, 8px, 12px, 16px… hardcodés --space-xs, --space-sm, --space-md…
Dynamisme JS JavaScript qui modifie les styles inline JS qui change une variable CSS, tout s'actualise
Responsive Media queries avec valeurs en dur Variables redéfinies par breakpoint
Compatibilité : Les Custom Properties sont supportées par 97%+ des navigateurs modernes. Seuls les anciens IE11 et certaines versions legacy ne les supportent pas.

2. Syntaxe détaillée

2.1 Custom Properties — Déclaration et usage

Déclarer une variable

Définition : Les variables CSS se déclarent avec le préfixe -- et s'utilisent avec la fonction var(). Elles doivent respecter la casse (sensible à la casse).
/* Déclaration — toujours dans un sélecteur */
:root {
  --ma-variable: valeur;
}

/* Exemples */
:root {
  --couleur-primaire: #2563eb;
  --taille-texte: 1rem;
  --espacement: 1.5rem;
  --rayon-bordure: 8px;
  --police-titre: 'Segoe UI', sans-serif;
  --ombre: 0 2px 10px rgba(0, 0, 0, 0.1);
  --transition: 0.3s ease;
}

Utiliser une variable

/* Avec la fonction var() */
.titre {
  color: var(--couleur-primaire);
  font-size: var(--taille-texte);
  font-family: var(--police-titre);
  padding: var(--espacement);
  border-radius: var(--rayon-bordure);
  box-shadow: var(--ombre);
  transition: all var(--transition);
}

Valeur par défaut (fallback)

Définition : La deuxième argument de var() est une valeur de secours utilisée si la variable n'est pas définie.
/* Fallback simple */
.titre {
  color: var(--couleur-primaire, #2563eb);
}

/* Fallback complexe */
.titre {
  font-size: var(--taille-titre, var(--taille-base, 1rem));
  /* Essaie --taille-titre, puis --taille-base, puis 1rem */
}

/* Fallback vide = valeur initiale */
.titre {
  color: var(--couleur-personnalise, );
  /* Si la variable n'existe pas, le navigateur utilise la valeur initiale */
}

Conventions de nommage

/* ✅ Bonnes conventions */
:root {
  --color-primary: #2563eb;
  --color-secondary: #7c3aed;
  --space-sm: 0.5rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --shadow-sm: 0 1px 3px rgba(0,0,0,0.1);
  --radius-md: 8px;
  --font-heading: 'Segoe UI', sans-serif;
}

/* ❌ Mauvaises conventions */
:root {
  --primaryColor: #2563eb;     /* camelCase (non CSS-like) */
  --BLUE: #2563eb;             /* MAJUSCULES (confus) */
  --1: 1rem;                   /* commence par un chiffre */
  --my color: #2563eb;         /* espace interdit */
  --couleur-primäire: #2563eb; /* caractères spéciaux */
}

Sensibilité à la casse

/* Les variables CSS sont SENSIBLES À LA CASSE */
:root {
  --couleur: blue;
  --Couleur: red;
  --COULEUR: green;
}

.el1 { color: var(--couleur);  } /* blue */
.el2 { color: var(--Couleur);  } /* red */
.el3 { color: var(--COULEUR);  } /* green */

2.2 Portée (Scope)

Définition : Les variables CSS suivent l'héritage CSS. Déclarées dans :root, elles sont accessibles partout. Déclarées sur un élément, elles sont accessibles à cet élément et à tous ses descendants.
/* Scope global — :root (équivalent à html) */
:root {
  --couleur: blue;
}

/* Scope local — un élément spécifique */
.card {
  --couleur-card: #f8fafc;
  background: var(--couleur-card); /* ✅ Visible ici */
}

.card .titre {
  color: var(--couleur);      /* ✅ Héritée de :root */
  background: var(--couleur-card); /* ✅ Héritée de .card */
}

/* Scope imbriqué — enfant d'un autre scope */
.card.sombre {
  --couleur-card: #1e293b;    /* Redéfinition locale */
  /* Tous les enfants de .card.sombre utiliseront cette nouvelle valeur */
}

Démonstration : Portée des variables

:root — Scope global

--couleur-primaire: #2563eb

Cette variable est accessible partout dans le document.

.enfant-a — Scope local

--couleur-secondaire: #7c3aed

Définie localement, héritée par les descendants.

.enfant-b — Redéfinition

--couleur-succes: #16a34a

On peut redéfinir une variable dans un scope enfant.

2.3 Shorthand et patterns

/* Raccourci pour définir toutes les variables d'un thème */
:root {
  /* Couleurs */
  --color-primary: #2563eb;
  --color-primary-hover: #1d4ed8;
  --color-secondary: #7c3aed;
  --color-success: #16a34a;
  --color-warning: #f59e0b;
  --color-error: #dc2626;
  --color-text: #1e293b;
  --color-text-secondary: #64748b;
  --color-bg: #ffffff;
  --color-bg-secondary: #f8fafc;
  --color-border: #e2e8f0;

  /* Typographie */
  --font-sans: 'Segoe UI', system-ui, sans-serif;
  --font-mono: 'Cascadia Code', 'Fira Code', monospace;
  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.25rem;
  --text-2xl: 1.5rem;
  --text-3xl: 2rem;

  /* Espacement */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-12: 3rem;

  /* Ombres */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
  --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);

  /* Bordures */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-full: 9999px;

  /* Transitions */
  --transition-fast: 0.15s ease;
  --transition-base: 0.3s ease;
  --transition-slow: 0.5s ease;
}

2.3 Thèmes — Light/Dark mode

Définition : Les variables CSS permettent de créer des thèmes en redéfinissant les mêmes noms de variables dans un scope différent (souvent via [data-theme] ou prefers-color-scheme).
/* Thème clair (défaut) */
[data-theme="light"] {
  --theme-bg: #ffffff;
  --theme-text: #1e293b;
  --theme-card-bg: #f8fafc;
  --theme-border: #e2e8f0;
  --theme-primary: #2563eb;
}

/* Thème sombre */
[data-theme="dark"] {
  --theme-bg: #0f172a;
  --theme-text: #f1f5f9;
  --theme-card-bg: #1e293b;
  --theme-border: #334155;
  --theme-primary: #60a5fa;
}

/* Utilisation — le même code CSS fonctionne pour les deux thèmes */
body {
  background: var(--theme-bg);
  color: var(--theme-text);
}

.card {
  background: var(--theme-card-bg);
  border: 1px solid var(--theme-border);
}

/* Support automatique selon les préférences système */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --theme-bg: #0f172a;
    --theme-text: #f1f5f9;
    /* ... */
  }
}

Démonstration : Thème Light/Dark avec variables

Mon Application

Carte avec variables

Cette carte utilise var(--theme-card-bg) pour son fond.

Thème dynamique

Cliquez sur le bouton pour changer de thème. Les variables se mettent à jour instantanément.

Accessibilité

Le thème respecte aussi les préférences système avec prefers-color-scheme.

3. Visualisation

3.1 Résumé des concepts de variables

╔═══════════════════════════════════════════════════════════════╗
║              CUSTOM PROPERTIES — RÉSUMÉ                      ║
╠═══════════════════════════════════════════════════════════════╣
║  DÉCLARATION:                                                ║
║    :root { --nom: valeur; }                                  ║
║    element { --nom: valeur; }  /* scope local */            ║
║                                                              ║
║  UTILISATION:                                                ║
║    property: var(--nom);                                     ║
║    property: var(--nom, fallback);  /* avec fallback */     ║
║    property: var(--nom, var(--autre, 1rem)); /* chaîné */   ║
║                                                              ║
║  MODIFICATION JS:                                            ║
║    element.style.setProperty('--nom', 'valeur');             ║
║    getComputedStyle(element).getPropertyValue('--nom');      ║
║                                                              ║
║  PORTÉE:                                                     ║
║    :root → global (tout le document)                         ║
║    .classe → local (cet élément + descendants)              ║
║    Héritage CSS classique                                    ║
╚═══════════════════════════════════════════════════════════════╝

╔═══════════════════════════════════════════════════════════════╗
║              ARCHITECTURE DE NOMMAGE                         ║
╠═══════════════════════════════════════════════════════════════╣
║  --color-primary:     #2563eb      Couleurs                 ║
║  --color-primary-hover: #1d4ed8    États des couleurs       ║
║  --space-sm:          0.5rem       Espacement                ║
║  --space-md:          1rem         Escalier d'espacement     ║
║  --text-lg:           1.125rem     Taille de police          ║
║  --shadow-md:         0 4px ...    Ombres                    ║
║  --radius-md:         8px          Bordures arrondies        ║
║  --transition-base:   0.3s ease    Transitions               ║
║  --font-heading:      sans-serif   Polices                   ║
║  --z-dropdown:        100          Z-index                    ║
╚═══════════════════════════════════════════════════════════════╝

Résumé visuel des variables CSS et leur architecture

3.2 Cascade et héritage des variables

HÉRITAGE DES VARIABLES CSS
═══════════════════════════

  :root { --couleur: blue; }
  │
  ├── body { --couleur: blue; }          ← hérite
  │   │
  │   ├── .header { --couleur: blue; }   ← hérite
  │   │   └── .nav { color: var(--couleur); }   → blue
  │   │
  │   └── .content { --couleur: red; }   ← REDÉFINI localement
  │       ├── .title { color: var(--couleur); }  → red
  │       └── .text { color: var(--couleur); }   → red
  │
  └── .sidebar { color: var(--couleur); } → blue (pas dans .content)

  La variable suit l'arbre DOM comme n'importe quelle propriété CSS.

L'héritage des variables suit l'arbre DOM

3.3 Design tokens — Système complet

Palette de couleurs avec tokens

Prim
--color-primary
#2563eb
Sec
--color-secondary
#7c3aed
OK
--color-success
#16a34a
Warn
--color-warning
#f59e0b
Err
--color-error
#dc2626
BG
--color-bg
#f8fafc

4. Exemples progressifs

4.1 Design system complet avec variables

/* ========== DESIGN TOKENS ========== */
:root {
  /* Couleurs */
  --color-primary: #2563eb;
  --color-primary-hover: #1d4ed8;
  --color-secondary: #7c3aed;
  --color-success: #16a34a;
  --color-warning: #f59e0b;
  --color-error: #dc2626;

  --color-text: #1e293b;
  --color-text-secondary: #64748b;
  --color-bg: #ffffff;
  --color-bg-secondary: #f8fafc;
  --color-border: #e2e8f0;

  /* Typographie */
  --font-sans: 'Segoe UI', system-ui, sans-serif;
  --font-mono: 'Cascadia Code', monospace;
  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.25rem;
  --text-2xl: 1.5rem;
  --text-3xl: 2rem;

  /* Espacement */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;

  /* Ombres */
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
  --shadow-md: 0 4px 6px -1px rgba(0,0,0,0.1);
  --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1);

  /* Bordures */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-full: 9999px;
}

/* ========== UTILISATION ========== */
.bouton {
  background: var(--color-primary);
  color: white;
  padding: var(--space-3) var(--space-6);
  border-radius: var(--radius-md);
  font-size: var(--text-base);
  font-family: var(--font-sans);
  box-shadow: var(--shadow-sm);
  transition: all var(--transition-base);
}

.bouton:hover {
  background: var(--color-primary-hover);
  box-shadow: var(--shadow-md);
}

.carte {
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  box-shadow: var(--shadow-sm);
}

.titre {
  font-family: var(--font-sans);
  font-size: var(--text-2xl);
  color: var(--color-text);
  margin-bottom: var(--space-4);
}

.sous-titre {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
}

Rendu : Design system avec tokens

Couleurs

primary
secondary
success
warning
error
bg
white

Espacement

4px
8px
12px
16px
24px
32px
48px

Typographie

--text-xsTaille extra small
--text-smTaille small
--text-baseTaille de base
--text-lgTaille large
--text-xlTaille extra large
--text-2xlTitre 2
--text-3xlTitre 1

Ombres

--shadow-sm
--shadow-md
--shadow-lg

4.2 Toggle Dark/Light

/* Définir les deux thèmes */
:root, [data-theme="light"] {
  --bg: #ffffff;
  --text: #1e293b;
  --card: #f8fafc;
  --border: #e2e8f0;
  --primary: #2563eb;
}

[data-theme="dark"] {
  --bg: #0f172a;
  --text: #f1f5f9;
  --card: #1e293b;
  --border: #334155;
  --primary: #60a5fa;
}

/* Utilisation */
body {
  background: var(--bg);
  color: var(--text);
  transition: background 0.3s, color 0.3s;
}

/* JavaScript pour switcher */
function toggleTheme() {
  const current = document.documentElement.getAttribute('data-theme');
  const next = current === 'dark' ? 'light' : 'dark';
  document.documentElement.setAttribute('data-theme', next);
  localStorage.setItem('theme', next); // Persistance
}

// Restaurer au chargement
const savedTheme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', savedTheme);

4.3 Theme toggle CSS-only

Toggle CSS pur (sans JavaScript pour le rendu)

Changer le thème de la page

4.4 Thématisation de composants

/* Composants avec variables pour le thème */
.bouton {
  --btn-bg: var(--color-primary);
  --btn-text: white;
  --btn-hover: var(--color-primary-hover);

  background: var(--btn-bg);
  color: var(--btn-text);
  padding: 0.6rem 1.25rem;
  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background 0.2s;
}

.bouton:hover { background: var(--btn-hover); }

/* Variantes via re-définition de variables */
.bouton-secondaire {
  --btn-bg: var(--color-bg-secondary);
  --btn-text: var(--color-text);
  --btn-hover: var(--color-border);
  border: 1px solid var(--color-border);
}

.bouton-succes {
  --btn-bg: var(--color-success);
  --btn-hover: #15803d;
}

/* Composant externe qui peut tout personnaliser */
.custom-theme {
  --btn-radius: 20px;          /* Personnalise le rayon */
  --btn-primary-bg: #ec4899;   /* Change la couleur */
  --btn-primary-hover: #db2777;
}

Rendu : Composants thématisés

4.5 Valeurs dynamiques via JavaScript

// Modifier une variable CSS via JavaScript
document.documentElement.style.setProperty('--couleur', '#7c3aed');

// Lire la valeur actuelle
const couleur = getComputedStyle(document.documentElement)
  .getPropertyValue('--couleur');

// Exemple : slider qui change la taille d'un élément
const slider = document.getElementById('size-slider');
slider.addEventListener('input', (e) => {
  document.documentElement.style.setProperty(
    '--dyn-size', `${e.target.value}px`
  );
});

// Exemple : changement de couleur en temps réel
const colorPicker = document.getElementById('color');
colorPicker.addEventListener('input', (e) => {
  document.documentElement.style.setProperty('--dyn-color', e.target.value);
});

Démonstration : Variables dynamiques avec sliders

80px
8px
#2563eb
Aperçu

4.6 Échelle typographique

/* Échelle basée sur la ratio 1.25 (Major Third) */
:root {
  --text-xs:   clamp(0.64rem, 0.58rem + 0.28vw, 0.8rem);
  --text-sm:   clamp(0.8rem, 0.73rem + 0.35vw, 1rem);
  --text-base: clamp(1rem, 0.91rem + 0.43vw, 1.25rem);
  --text-lg:   clamp(1.25rem, 1.14rem + 0.54vw, 1.563rem);
  --text-xl:   clamp(1.563rem, 1.43rem + 0.67vw, 1.953rem);
  --text-2xl:  clamp(1.953rem, 1.78rem + 0.84vw, 2.441rem);
  --text-3xl:  clamp(2.441rem, 2.23rem + 1.05vw, 3.052rem);
  --text-4xl:  clamp(3.052rem, 2.79rem + 1.32vw, 3.815rem);
}

/* Utilisation */
h1 { font-size: var(--text-4xl); }
h2 { font-size: var(--text-3xl); }
h3 { font-size: var(--text-2xl); }
h4 { font-size: var(--text-xl); }
p  { font-size: var(--text-base); }
small { font-size: var(--text-sm); }

Rendu : Échelle typographique

--text-xs Extra small — Le quick brown fox
--text-sm Small — The quick brown fox
--text-base Base — The quick brown fox jumps over
--text-lg Large — The quick brown fox jumps over the lazy dog
--text-xl Extra large — The quick brown fox jumps over the lazy dog
--text-2xl Titre 2 — The quick brown fox
--text-3xl Titre 1 — The quick brown fox

4.7 Système d'espacement

/* Escalier d'espacement (base 4px) */
:root {
  --space-0: 0;
  --space-1: 0.25rem;   /* 4px */
  --space-2: 0.5rem;    /* 8px */
  --space-3: 0.75rem;   /* 12px */
  --space-4: 1rem;      /* 16px */
  --space-5: 1.25rem;   /* 20px */
  --space-6: 1.5rem;    /* 24px */
  --space-8: 2rem;      /* 32px */
  --space-10: 2.5rem;   /* 40px */
  --space-12: 3rem;     /* 48px */
  --space-16: 4rem;     /* 64px */
  --space-20: 5rem;     /* 80px */
  --space-24: 6rem;     /* 96px */
}

/* Utilisation cohérente */
.card { padding: var(--space-6); }
.card-title { margin-bottom: var(--space-4); }
.card-text { margin-bottom: var(--space-2); }
.section { padding: var(--space-12) var(--space-6); }

Rendu : Échelle d'espacement

4px
8px
12px
16px
24px
32px
48px
64px

4.8 Variables responsive

/* Breakpoints en variables (utilisation en JS ou media queries) */
:root {
  --bp-sm: 640px;
  --bp-md: 768px;
  --bp-lg: 1024px;
  --bp-xl: 1280px;
  --bp-2xl: 1536px;
}

/* Les variables ne fonctionnent PAS dans les media queries CSS nativement */
/* MAIS on peut les utiliser en JS */
@media (min-width: 768px) {
  /* Il faut utiliser la valeur en dur ici */
  .grid { grid-template-columns: repeat(3, 1fr); }
}

/* En JavaScript, on peut utiliser les variables */
const bp = getComputedStyle(document.documentElement)
  .getPropertyValue('--bp-md').trim();
window.matchMedia(`(min-width: ${bp})`);

4.9 Générateur de palette de couleurs

/* Palette basée sur une couleur primaire */
:root {
  --color-primary-50:  #eff6ff;
  --color-primary-100: #dbeafe;
  --color-primary-200: #bfdbfe;
  --color-primary-300: #93c5fd;
  --color-primary-400: #60a5fa;
  --color-primary-500: #2563eb;
  --color-primary-600: #1d4ed8;
  --color-primary-700: #1e40af;
  --color-primary-800: #1e3a8a;
  --color-primary-900: #1e3a5f;
}

/* Utilisation selon le contexte */
.bouton { background: var(--color-primary-500); }  /* Fond principal */
.bouton:hover { background: var(--color-primary-600); } /* Plus foncé */
.alerte { background: var(--color-primary-50); }  /* Très clair */
.bordure { border-color: var(--color-primary-200); } /* Subtil */
.texte { color: var(--color-primary-700); }       /* Foncé */

Rendu : Palette de couleurs

50
100
200
300
400
500
600
700
800
900

4.10 Système d'ombres

/* Système d'ombres progressif */
:root {
  --shadow-xs:  0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-sm:  0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
  --shadow-md:  0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
  --shadow-lg:  0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
  --shadow-xl:  0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
  --shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
  --shadow-inner: inset 0 2px 4px rgba(0, 0, 0, 0.06);

  /* Ombres colorées */
  --shadow-primary: 0 4px 14px rgba(37, 99, 235, 0.25);
  --shadow-success: 0 4px 14px rgba(22, 163, 74, 0.25);
  --shadow-error:   0 4px 14px rgba(220, 38, 38, 0.25);
}

/* Utilisation */
.carte { box-shadow: var(--shadow-sm); }
.carte:hover { box-shadow: var(--shadow-lg); }
.bouton { box-shadow: var(--shadow-primary); }
.input:focus { box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1); }

Rendu : Système d'ombres

--shadow-xs
--shadow-sm
--shadow-md
--shadow-lg
--shadow-xl

4.11 Architecture de nommage

Convention de nommage recommandée

Couleurs

  • --color-primary
  • --color-primary-hover
  • --color-secondary
  • --color-success
  • --color-error
  • --color-text
  • --color-bg
  • --color-border

Espacement

  • --space-1 (4px)
  • --space-2 (8px)
  • --space-3 (12px)
  • --space-4 (16px)
  • --space-6 (24px)
  • --space-8 (32px)
  • --space-12 (48px)
  • --space-16 (64px)

Typographie

  • --font-sans
  • --font-mono
  • --text-xs / sm / base
  • --text-lg / xl
  • --text-2xl / 3xl
  • --leading-tight / normal
  • --tracking-tight / wide

Composants

  • --btn-radius
  • --btn-primary-bg
  • --card-padding
  • --input-border
  • --modal-max-width
  • --nav-height

5. Erreurs fréquentes

Erreur 1 : Oublier le préfixe -- lors de la déclaration
/* FAUX — le préfixe -- est OBLIGATOIRE à la déclaration */
:root {
  couleur: #2563eb;          /* C'est une propriété CSS normale, pas une variable ! */
}

/* CORRECT */
:root {
  --couleur: #2563eb;        /* Préfixe -- obligatoire */
}

/* Utilisation : var(--couleur) */
Erreur 2 : Utiliser le préfixe -- dans var()
/* FAUX — var() cherche -- dans la variable, pas besoin de double -- */
.el { color: var(--couleur); }  /* OK */
.el { color: var(----couleur); } /* FAUX — double -- */

/* CORRECT — var() ajoute automatiquement le -- */
.el { color: var(--couleur); }
Erreur 3 : Confondre déclaration et utilisation
/* FAUX — var() ne peut pas être utilisé à gauche de : */
.el {
  var(--couleur): #2563eb;  /* IMPOSSIBLE */
}

/* CORRECT — déclarer dans :root, utiliser dans le sélecteur */
:root { --couleur: #2563eb; }
.el { color: var(--couleur); }
Erreur 4 : Ne pas fourni de fallback
/* RISQUÉ — si la variable n'existe pas, la propriété est invalide */
.el {
  color: var(--couleur-personnalise);
}

/* SÉCURISÉ — toujours un fallback */
.el {
  color: var(--couleur-personnalise, #2563eb);
}

/* OU avec fallback chaîné */
.el {
  color: var(--couleur-personnalise, var(--couleur-primaire, #2563eb));
}
Erreur 5 : Utiliser des variables dans les media queries
/* FAUX — les variables ne fonctionnent PAS dans les media queries */
/* Les media queries sont évaluées AVANT les variables */
@media (min-width: --bp-md) {   /* NE FONCTIONNE PAS */
  .grid { grid-template-columns: repeat(3, 1fr); }
}

/* CORRECT — utiliser les valeurs en dur */
@media (min-width: 768px) {
  .grid { grid-template-columns: repeat(3, 1fr); }
}

/* Astuce : utiliser CSS nesting ou des classes */
:root { --mobile: 0; --desktop: 1; }
.grid { --cols: 1; }
@media (min-width: 768px) { .grid { --cols: 3; } }
.grid { grid-template-columns: repeat(var(--cols), 1fr); }
Erreur 6 : Abuser des variables pour tout
/* PAS EFFICACE — over-engineering */
:root {
  --padding-top: 1rem;
  --padding-right: 1rem;
  --padding-bottom: 1rem;
  --padding-left: 1rem;
}
.el { padding: var(--padding-top) var(--padding-right) var(--padding-bottom) var(--padding-left); }

/* EFFICACE — garder les variables pour les valeurs réellement réutilisées */
:root {
  --space-4: 1rem;
}
.el { padding: var(--space-4); }
/* Ou simplement : .el { padding: 1rem; } si c'est unique */
Erreur 7 : Casse inconsistante
/* INCOHÉRENT — mélange de conventions */
:root {
  --couleur-primaire: #2563eb;   /* kebab-case */
  --ColorSecondary: #7c3aed;     /* PascalCase */
  --SPACE_MD: 1rem;              /* SCREAMING_SNAKE */
}

/* CORRECT — adopter une convention unique */
:root {
  --color-primary: #2563eb;     /* kebab-case (recommandé) */
  --color-secondary: #7c3aed;
  --space-md: 1rem;
}
Erreur 8 : Oublier que les variables sont héritées
/* SURPRISE — la variable d'un parent affecte les enfants */
.parent {
  --text-size: 2rem;  /* Déclarée ici */
}
.parent .enfant {
  /* --text-size est aussi 2rem ! Peut être inattendu */
  font-size: var(--text-size); /* 2rem — hérité du parent */
}

/* CORRECT — être conscient de la portée ou redéfinir */
.enfant {
  --text-size: 1rem;  /* Redéfinition locale */
  font-size: var(--text-size); /* 1rem — valeur locale */
}

6. Bonnes pratiques

Les règles d'or des variables CSS :
  • Déclarer toutes les variables dans :root pour un accès global et une maintenance facile.
  • Adopter une convention de nommage (kebab-case recommandé : --color-primary).
  • Toujours fournir un fallback dans var() pour éviter les surprises.
  • Regrouper par catégorie (couleurs, espacement, typo…) pour une lisibilité optimale.
  • Créer un système de design tokens complet : couleurs, espaces, ombres, transitions.
  • Utiliser les variables pour les thèmes — redéfinir les mêmes noms dans [data-theme].
  • Utiliser JS pour le dynamismestyle.setProperty() pour modifier en runtime.
  • Documenter les variables dans un fichier CSS dédié ou un guide de style.

Architecture recommandée

Structure de fichier pour un design system :
/* 1. tokens.css — Définition de toutes les variables */
@import url('./tokens.css');

/* tokens.css contient : */
:root {
  /* === COULEURS === */
  --color-primary-50: #eff6ff;
  /* ... palette complète ... */

  /* === ESPACEMENT === */
  --space-1: 0.25rem;
  /* ... escalier complet ... */

  /* === TYPOGRAPHIE === */
  --font-sans: 'Segoe UI', system-ui, sans-serif;
  /* ... échelle complète ... */

  /* === OMBRES === */
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
  /* ... système complet ... */

  /* === TRANSITIONS === */
  --ease-in: cubic-bezier(0.4, 0, 1, 1);
  --ease-out: cubic-bezier(0, 0, 0.2, 1);
  --duration-fast: 150ms;
  --duration-base: 300ms;
}

/* 2. dark.css — Override pour le thème sombre */
[data-theme="dark"] {
  --color-primary-500: #60a5fa;
  /* ... overrides ... */
}

/* 3. Les composants utilisent uniquement les tokens */
.bouton {
  background: var(--color-primary-500);
  padding: var(--space-3) var(--space-6);
  font-family: var(--font-sans);
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--duration-base) var(--ease-out);
}

Quand utiliser les variables

Utiliser une variable quand… Exemple
La valeur est utilisée 2+ fois --color-primary
La valeur peut changer (thème) --color-bg
La valeur est modifiable par JS --dyn-size
C'est un token de design system --space-4
C'est une constante documentée --nav-height: 64px

7. Mini-défi

Défi 1 : Thème clair/sombre

Créez un système de thème avec data-theme="light" et data-theme="dark". Le thème sombre doit avoir un fond #0f172a et un texte #f1f5f9.

:root, [data-theme="light"] {
  --bg: #ffffff;
  --text: #1e293b;
  --card: #f8fafc;
}

[data-theme="dark"] {
  --bg: #0f172a;
  --text: #f1f5f9;
  --card: #1e293b;
}

body {
  background: var(--bg);
  color: var(--text);
  transition: background 0.3s, color 0.3s;
}

Défi 2 : Composant bouton avec variants

Créez un bouton qui utilise des variables pour ses couleurs, et 3 variantes (primaire, secondaire, danger) en redéfinissant les variables.

.btn {
  --btn-bg: var(--color-primary);
  --btn-text: white;

  background: var(--btn-bg);
  color: var(--btn-text);
  padding: var(--space-3) var(--space-6);
  border-radius: var(--radius-md);
}

.btn-secondary {
  --btn-bg: var(--color-gray-100);
  --btn-text: var(--color-gray-900);
}

.btn-danger {
  --btn-bg: var(--color-error);
  --btn-text: white;
}

Défi 3 : Palette dynamique

Créez un color picker qui modifie en temps réel la couleur d'un élément via style.setProperty().

const picker = document.getElementById('color');
picker.addEventListener('input', (e) => {
  document.documentElement.style.setProperty('--color', e.target.value);
});

/* HTML */
/* <input type="color" id="color" value="#2563eb"> */
/* <div class="preview" style="background: var(--color)"> */

/* CSS */
/* .preview { transition: background 0.2s; } */

Défi 4 : Design system minimal

Créez un fichier de tokens avec : 5 couleurs, 5 espacements, 3 tailles de texte, 3 ombres, 3 rayons de bordure.

:root {
  /* Couleurs */
  --c-1: #2563eb;
  --c-2: #7c3aed;
  --c-3: #16a34a;
  --c-4: #f59e0b;
  --c-5: #dc2626;

  /* Espacement */
  --s-1: 0.25rem;
  --s-2: 0.5rem;
  --s-3: 1rem;
  --s-4: 1.5rem;
  --s-5: 2rem;

  /* Texte */
  --t-sm: 0.875rem;
  --t-md: 1rem;
  --t-lg: 1.25rem;

  /* Ombres */
  --sh-sm: 0 1px 2px rgba(0,0,0,0.05);
  --sh-md: 0 4px 6px rgba(0,0,0,0.1);
  --sh-lg: 0 10px 15px rgba(0,0,0,0.1);

  /* Rayons */
  --r-sm: 4px;
  --r-md: 8px;
  --r-lg: 16px;
}

Défi 5 : Typographie responsive

Créez une échelle typographique avec clamp() qui s'adapte à la taille de l'écran, en utilisant des variables.

:root {
  --text-xs: clamp(0.7rem, 0.65rem + 0.25vw, 0.8rem);
  --text-sm: clamp(0.8rem, 0.75rem + 0.3vw, 0.9rem);
  --text-base: clamp(0.95rem, 0.9rem + 0.35vw, 1.1rem);
  --text-lg: clamp(1.15rem, 1.05rem + 0.45vw, 1.35rem);
  --text-xl: clamp(1.4rem, 1.2rem + 0.8vw, 1.8rem);
  --text-2xl: clamp(1.7rem, 1.4rem + 1.2vw, 2.4rem);
  --text-3xl: clamp(2rem, 1.5rem + 2vw, 3.2rem);
}

h1 { font-size: var(--text-3xl); }
h2 { font-size: var(--text-2xl); }
p  { font-size: var(--text-base); }

8. Projets pratiques

Projet 1 : Design system complet

Créez un design system de A à Z avec :

  • Fichier tokens avec couleurs, espaces, typo, ombres, transitions
  • Thème clair/sombre basé sur data-theme
  • Composants bouton, carte, input, badge utilisant les tokens
  • Toggle de thème fonctionnel avec JavaScript
  • Persistance du thème avec localStorage

Projet 2 : Color palette generator

Créez un générateur de palette avec :

  • Un color picker qui définit la couleur principale
  • Calcul automatique des nuances (50 à 900)
  • Affichage de la palette complète avec noms
  • Bouton "Copier" pour chaque nuance
  • Export du CSS généré

Projet 3 : Thème switcher avancé

Créez un système de thème multi-thèmes avec :

  • 3 thèmes : clair, sombre, contraste élevé
  • Toggle CSS-only avec checkbox
  • Transition fluide entre les thèmes
  • prefers-color-scheme comme valeur par défaut
  • Persistance avec localStorage

9. Révision

Récapitulatif des concepts clés

Concept Syntaxe Description
Déclaration --nom: valeur; Crée une variable dans un sélecteur
Utilisation var(--nom) Référence la variable
Fallback var(--nom, défaut) Valeur par défaut si non définie
Scope global :root { --x: val; } Accessible partout
Scope local .el { --x: val; } Accessible dans .el et ses descendants
Modification JS style.setProperty('--x', val) Modifier une variable en runtime
Lecture JS getComputedStyle(el).getPropertyValue('--x') Lire la valeur actuelle
Thème [data-theme="dark"] { --x: val; } Redéfinition par thème
Héritage Naturel (comme les props CSS) Les enfants héritent des variables parent
Casse Sensible à la casse --Color--color

Quiz rapide

  1. Comment déclarer une variable CSS accessible globalement ?
  2. Comment utiliser une variable avec une valeur par défaut ?
  3. Les variables CSS sont-elles sensibles à la casse ?
  4. Comment modifier une variable CSS via JavaScript ?
  5. Les variables fonctionnent-elles dans les media queries ?
  6. Quelle est la différence de portée entre :root et .classe ?
  7. Comment créer un thème sombre avec des variables ?
  8. Pourquoi toujours fournir un fallback dans var() ?
  9. Quelle convention de nommage est recommandée pour les variables ?
  10. Les variables CSS sont-elles héritées par les enfants ?
Réponses :
  1. :root { --ma-variable: valeur; }:root est l'équivalent de html, scope global.
  2. var(--ma-variable, valeur-par-defaut) — le 2e argument est le fallback.
  3. Oui, --color et --Color sont deux variables différentes.
  4. element.style.setProperty('--nom', 'valeur')
  5. Non, les media queries sont évaluées avant les variables. Il faut utiliser des valeurs en dur.
  6. :root = global (tout le document hérite), .classe = local (cet élément et ses enfants).
  7. En redéfinissant les mêmes variables dans [data-theme="dark"] avec des valeurs adaptées au fond sombre.
  8. Si la variable n'existe pas et qu'il n'y a pas de fallback, la propriété CSS est considérée invalide.
  9. kebab-case : --color-primary, --space-md, --shadow-lg.
  10. Oui, les variables CSS suivent l'héritage CSS classique — les enfants héritent des variables de leur parent.

Cheat sheet rapide

/* Déclarer */
:root {
  --couleur: #2563eb;
  --espace: 1rem;
  --rayon: 8px;
}

/* Utiliser */
.el {
  color: var(--couleur);
  padding: var(--espace);
  border-radius: var(--rayon);
}

/* Avec fallback */
.el { color: var(--couleur, #2563eb); }

/* Scope local */
.card {
  --card-bg: #f8fafc;
  background: var(--card-bg);
}

/* Thème sombre */
[data-theme="dark"] {
  --couleur: #60a5fa;
}

/* Modifier via JS */
document.documentElement.style.setProperty('--couleur', '#7c3aed');

/* Lire via JS */
const val = getComputedStyle(document.documentElement)
  .getPropertyValue('--couleur').trim();

/* Animation avec variables */
.el {
  transition: color var(--duration, 0.3s) var(--ease, ease);
}

/* Avec clamp pour responsive */
:root {
  --text-lg: clamp(1rem, 0.9rem + 0.5vw, 1.2rem);
}