Cours JavaScript et React · Module 14: Styles avancés

Les variables CSS et les thèmes dynamiques dans React - Personnalisation cosmique

5 min de lecture
Dans cette leçon8

Pilote, imagine un vaisseau spatial qui change toute la palette de couleurs du cockpit d'un seul interrupteur -- du mode jour au mode nuit, du mode combat au mode exploration. Dans le monde de React, c'est exactement ce que font les variables CSS (aussi appelées CSS Custom Properties). Elles te permettent de définir des valeurs à un seul endroit et de les utiliser dans tout le système de stylage -- et changer une seule variable met instantanément à jour tous les éléments qui l'utilisent.

Que sont les variables CSS ?

Les variables CSS (Custom Properties) sont des propriétés CSS spéciales qui commencent par deux tirets --. Tu les définis une fois, puis tu les références à l'aide de la fonction var() :

1:root {
2  --color-primary: #7c4dff;
3  --color-background: #0a0a2e;
4  --color-surface: #1a1a4e;
5  --color-text: #e0e0e0;
6  --spacing-md: 16px;
7  --radius-lg: 12px;
8}
9
10.card {
11  background: var(--color-surface);
12  color: var(--color-text);
13  padding: var(--spacing-md);
14  border-radius: var(--radius-lg);
15}

La différence clé avec les variables en SASS ou en JavaScript -- les variables CSS sont réactives. Quand tu changes leur valeur à l'exécution, le navigateur ré-affiche immédiatement tous les éléments qui les utilisent -- sans recharger la page et sans re-render de React.

Variables CSS + état React -- changer de thème

Voici la façon la plus simple de changer de thème dans React -- on définit les variables CSS sur le style de l'élément, et tout le CSS réagit automatiquement :

1function App() {
2  const [isDark, setIsDark] = useState(true);
3
4  const themeVars = isDark ? {
5    '--color-bg': '#0a0a2e',
6    '--color-surface': '#1a1a4e',
7    '--color-text': '#e0e0e0',
8    '--color-primary': '#7c4dff',
9    '--color-border': '#3949ab',
10  } : {
11    '--color-bg': '#f5f5f5',
12    '--color-surface': '#ffffff',
13    '--color-text': '#212121',
14    '--color-primary': '#5c6bc0',
15    '--color-border': '#e0e0e0',
16  };
17
18  return (
19    <div style={themeVars} className="app">
20      <button onClick={() => setIsDark(!isDark)}>
21        {isDark ? 'Light Mode' : 'Dark Mode'}
22      </button>
23      <div className="card">Mission Card</div>
24    </div>
25  );
26}

En CSS, on utilise ces variables normalement :

1.app {
2  background: var(--color-bg);
3  color: var(--color-text);
4}
5.card {
6  background: var(--color-surface);
7  border: 1px solid var(--color-border);
8}

Remarque -- changer l'état React met à jour les variables CSS, et le navigateur met à jour l'apparence tout seul. Pas besoin de recalculer les styles en JavaScript ou de créer de nouveaux objets de style.

Le pattern ThemeProvider avec les variables CSS

Dans les applications plus grandes, il vaut la peine de créer un composant ThemeProvider dédié qui enveloppe toute l'application et gère le thème :

1const themes = {
2  cosmos: {
3    '--color-bg': '#0a0a2e',
4    '--color-surface': '#1a1a4e',
5    '--color-primary': '#7c4dff',
6    '--color-text': '#e0e0e0',
7  },
8  nebula: {
9    '--color-bg': '#1a0a2e',
10    '--color-surface': '#2a1a4e',
11    '--color-primary': '#e040fb',
12    '--color-text': '#f3e5f5',
13  },
14};
15
16function ThemeProvider({ theme, children }) {
17  return (
18    <div style={themes[theme]} className="theme-root">
19      {children}
20    </div>
21  );
22}
23
24function App() {
25  const [theme, setTheme] = useState('cosmos');
26
27  return (
28    <ThemeProvider theme={theme}>
29      <button onClick={() => setTheme(t => t === 'cosmos' ? 'nebula' : 'cosmos')}>
30        Switch Theme
31      </button>
32      <Dashboard />
33    </ThemeProvider>
34  );
35}

Mode sombre/clair -- implémentation

Voici un pattern complet pour implémenter le mode sombre et le mode clair :

1const darkTheme = {
2  '--color-bg': '#0a0a2e',
3  '--color-surface': '#1a1a4e',
4  '--color-text': '#e0e0e0',
5  '--color-text-muted': '#9e9e9e',
6  '--color-primary': '#7c4dff',
7  '--color-success': '#00e676',
8  '--color-border': '#3949ab',
9  '--shadow': '0 4px 12px rgba(0,0,0,0.3)',
10};
11
12const lightTheme = {
13  '--color-bg': '#f5f5f5',
14  '--color-surface': '#ffffff',
15  '--color-text': '#212121',
16  '--color-text-muted': '#757575',
17  '--color-primary': '#5c6bc0',
18  '--color-success': '#4caf50',
19  '--color-border': '#e0e0e0',
20  '--shadow': '0 4px 12px rgba(0,0,0,0.1)',
21};

L'avantage clé -- c'est le CSS qui fait tout le travail. React ne fait que changer les variables, et le navigateur ré-affiche les styles nativement, sans opérations JavaScript coûteuses.

Valeur par défaut dans var()

La fonction var() accepte un deuxième argument -- une valeur par défaut utilisée quand la variable n'est pas définie :

1.card {
2  background: var(--color-surface, #1a1a4e);
3  padding: var(--spacing-md, 16px);
4}

C'est utile pour la sécurité -- le composant fonctionnera même sans ThemeProvider.

Détecter les préférences système -- prefers-color-scheme

L'utilisateur peut avoir défini un thème sombre ou clair au niveau du système d'exploitation. Dans React, on peut le détecter et adapter automatiquement le thème de l'application :

1function useSystemTheme() {
2  const [isDark, setIsDark] = useState(
3    () => window.matchMedia('(prefers-color-scheme: dark)').matches
4  );
5
6  useEffect(() => {
7    const mq = window.matchMedia('(prefers-color-scheme: dark)');
8    const handler = (e) => setIsDark(e.matches);
9    mq.addEventListener('change', handler);
10    return () => mq.removeEventListener('change', handler);
11  }, []);
12
13  return isDark;
14}
15
16function App() {
17  const systemIsDark = useSystemTheme();
18  const [isDark, setIsDark] = useState(systemIsDark);
19
20  return (
21    <div style={isDark ? darkTheme : lightTheme} className="app">
22      <button onClick={() => setIsDark(!isDark)}>
23        {isDark ? 'Light Mode' : 'Dark Mode'}
24      </button>
25    </div>
26  );
27}

Le hook useSystemTheme écoute la media query prefers-color-scheme: dark -- quand l'utilisateur change ses paramètres système, l'application réagit immédiatement.

Injection de styles à l'exécution -- injecter du CSS dynamiquement

Parfois, tu dois injecter des styles CSS dynamiquement à l'exécution -- par exemple, une couleur d'accentuation choisie par l'utilisateur :

1function injectCSSVariables(vars) {
2  Object.entries(vars).forEach(([key, value]) => {
3    document.documentElement.style.setProperty(key, value);
4  });
5}
6
7function AccentPicker() {
8  const handleAccent = (color) => {
9    injectCSSVariables({
10      '--color-accent': color,
11      '--color-accent-light': color + '33',
12    });
13  };
14
15  return (
16    <div>
17      <button onClick={() => handleAccent('#7c4dff')}>Purple</button>
18      <button onClick={() => handleAccent('#00e676')}>Green</button>
19      <button onClick={() => handleAccent('#ff5722')}>Orange</button>
20    </div>
21  );
22}

document.documentElement.style.setProperty définit une variable CSS globalement sur l'élément :root -- tout le CSS réagit immédiatement.

Quand utiliser les variables CSS dans React ?

  • Changement de thème (mode sombre/clair) -- changer un seul objet change toute l'apparence
  • Détection des préférences système -- adaptation automatique aux paramètres de l'utilisateur
  • Couleurs dynamiques -- par exemple, une couleur d'accentuation choisie par l'utilisateur
  • Injection à l'exécution -- injecter des variables sans re-render de React
  • Animations -- les transitions CSS fonctionnent en douceur avec les variables
  • Performance -- changer une variable CSS ne provoque pas de re-render de React
Code de cette leçon : App.jsx
1import React, { useState } from 'react';
2import './styles.css';
3
4// Définition des thèmes sous forme d'objets de variables CSS
5const darkTheme = {
6  '--color-bg': '#0a0a2e',
7  '--color-surface': '#1a1a4e',
8  '--color-text': '#e0e0e0',
9  '--color-text-muted': '#9e9e9e',
10  '--color-primary': '#7c4dff',
11  '--color-success': '#00e676',
12  '--color-border': '#3949ab',
13  '--shadow': '0 4px 12px rgba(0,0,0,0.3)',
14};
15
16const lightTheme = {
17  '--color-bg': '#f5f5f5',
18  '--color-surface': '#ffffff',
19  '--color-text': '#212121',
20  '--color-text-muted': '#757575',
21  '--color-primary': '#5c6bc0',
22  '--color-success': '#4caf50',
23  '--color-border': '#e0e0e0',
24  '--shadow': '0 4px 12px rgba(0,0,0,0.1)',
25};
26
27function StatusCard({ label, status }) {
28  return (
29    <div className="status-card">
30      <span className="status-label">{label}</span>
31      <span className={`status-badge status-${status}`}>
32        {status.toUpperCase()}
33      </span>
34    </div>
35  );
36}
37
38export default function App() {
39  const [isDark, setIsDark] = useState(true);
40
41  return (
42    <div style={isDark ? darkTheme : lightTheme} className="app">
43      <div className="header">
44        <h1 className="heading">CSS Variables + React</h1>
45        <button className="toggle-btn" onClick={() => setIsDark(!isDark)}>
46          {isDark ? 'Mode jour' : 'Mode nuit'}
47        </button>
48      </div>
49      <p className="subtitle">Changement de thème par modification des variables CSS</p>
50
51      <div className="card">
52        <h2 className="card-title">Panneau de statut de la mission</h2>
53        <StatusCard label="Moteur principal" status="active" />
54        <StatusCard label="Bouclier thermique" status="warning" />
55        <StatusCard label="Carburant de réserve" status="critical" />
56      </div>
57
58      <div className="card">
59        <h2 className="card-title">Comment ça marche ?</h2>
60        <p className="info-text">
61          React change l'objet style avec les variables CSS.
62          Le navigateur met immédiatement à jour tous les éléments
63          qui utilisent var(--color-...) -- sans re-render des composants !
64        </p>
65      </div>
66    </div>
67  );
68}

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Comment changer de thème (sombre/clair) dans React en utilisant les variables CSS ?

Articles utiles