kalvn logo kalvn

Créer un thème sombre facilement

Créer un site web en 2025 sans proposer de thème sombre c'est condamner à l'aveuglement cette frange de la population qui parcourt le Web essentiellement dans le noir, sous la couette ou encore en boîte de nuit.

Bref, c'est pratiquement inenvisageable. Le souci c'est que créer un thème sombre qui tient la route n'est pas une mince affaire. J'ai eu le loisir de m'en rendre compte en créant ce site que vos yeux délicats parcourent en ce moment.

Le CSS moderne est là pour vous

L'avènement des thèmes sombre commence à dater un peu. Ça fait bien 5 ans qu'il est devenu bon ton de proposer thème clair et thème sombre lorsque l'on créé un site web ou une application. C'était initialement périlleux sans un minimum de JavaScript, mais il est aujourd'hui possible de s'en passer totalement.

L'usage de la fonction light-dark combinée à la propriété color-scheme est ce qui m'est apparu comme le plus facile pour commencer :

:root {
  color-scheme: light dark;
}

body {
  color: light-dark(#333b3c, #efefec);
  background-color: light-dark(#efedea, #223a2c);
}

Ces 8 lignes suffisent comme base.

Difficile de faire plus simple.

Il vous suffit d'utiliser la fonction light-dark() à chaque fois qu'une couleur doit s'adapter au thème.

Saupoudrez de quelques variables CSS

Cependant lorsque la quantité de code CSS croît, cela devient difficile à maintenir.

Heureusement, là encore, le CSS moderne — et plus particulièrement les variables — est là pour nous faciliter la vie :

:root {
  color-scheme: light dark;

  --color-background-dark: #0a0a0a;
  --color-background-light: #fff;
  --color-background: light-dark(var(--color-background-light), var(--color-background-dark));

  --color-text-dark: #E8E5E3;
  --color-text-light: #171717;
  --color-text: light-dark(var(--color-text-light), var(--color-text-dark));
}

body {
  background-color: var(--color-background);
  color: var(--color-text);
}

C'est un peu plus verbeux initialement, mais ça simplifie grandement l'usage. Une fois vos variantes de couleur déclarées dans le bloc :root, il vous suffit d'utiliser dans vos composants la variable résultante de la fonction light-dark(), en l'occurrence var(--color-background) et var(--color-text).

Appliquez cette recette pour toutes vos couleurs et n'y songez plus.

Ajouter un theme switcher

Désolé pour l'anglicisme mais je ne trouve pas de traduction satisfaisante.

Jusqu'à maintenant, on a ajouté le code nécessaire pour s'adapter au thème choisi par l'utilisateur au niveau de son système ou de son navigateur, mais il est impossible d'en changer dynamiquement lors de la navigation.

Pour ce faire, on devra passer par la case JavaScript, mais quelques lignes suffiront.

Il existe de multiples possibilité d'y parvenir mais cet excellent article sur HTMHell propose une solution que j'ai trouvé particulièrement séduisante.

L'idée principale est de déplacer l'instruction color-scheme: light dark; du CSS au <head> de la page, et de faire varier ce paramètre entre light et dark avec un bouton.

:root {
  color-scheme: light dark; 

  --color-background-dark: #0a0a0a;
  --color-background-light: #fff;
  --color-background: light-dark(var(--color-background-light), var(--color-background-dark));

  --color-text-dark: #E8E5E3;
  --color-text-light: #171717;
  --color-text: light-dark(var(--color-text-light), var(--color-text-dark));
}

Dans le code HTML :

<!doctype html>
<html lang="fr">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Sports Almanac 1950-2000</title>
    <meta name="color-scheme" content="dark light">

Ajoutez ensuite un bouton pour chaque thème :

<button class="switch-theme" data-theme="light">Thème clair</button>
<button class="switch-theme" data-theme="dark">Thème sombre</button>

Puis un peu de JavaScript pour modifier le color-scheme dynamiquement :

const colorScheme = document.querySelector('meta[name=color-scheme]');
const switchThemeButtons = document.querySelectorAll('.switch-theme');

switchThemeButtons.forEach((button) => {
  button.addEventListener('click', () => {
    colorScheme.content = button.dataset.theme;
  });
});

Et voilà le résultat :

Sauvegarder le choix

Pour avoir le theme switcher parfait, il manque une dernière chose : mémoriser le choix de l'utilisateur. L'utilisation du Local Storage est parfait pour cela.

Il y a deux scénarios à prendre en considération :

  1. Lors du changement de thème, il faut sauvegarder le choix de l'utilisateur dans le Local Storage.
  2. Lors du chargement du site, il faut restaurer le choix qu'à fait l'utilisateur précédemment ou basculer sur la valeur par défaut.

Sauvegarder

Une seule ligne à ajouter:

const colorScheme = document.querySelector('meta[name=color-scheme]');
const switchThemeButtons = document.querySelectorAll('.switch-theme');

switchThemeButtons.forEach((button) => {
  button.addEventListener('click', () => {
    colorScheme.content = button.dataset.theme;
    localStorage.setItem('colorScheme', button.dataset.theme); 
  });
});

Restaurer

Il y a une petite subtilité à prendre en compte pour assurer une expérience optimale : il faut restaurer le choix de l'utilisateur avant que la page ne s'affiche, sans quoi il risque d'y avoir un effet de clignotement. Le site va d'abord s'afficher avec le thème par défaut, et après quelques millisecondes il va passer au thème sauvegardé. Pas terrible.

Pour éviter cet effet désagréable, ce code doit idéalement être exécuté au tout début du <body> :

<body>
  <script>
    const savedColorScheme = localStorage.getItem('colorScheme');
    if (['light', 'dark', 'dark light'].includes(savedColorScheme)) {
      const colorScheme = document.querySelector('meta[name=color-scheme]');
      colorScheme.content = savedColorScheme;
    }
  </script>
  <!-- Reste de la page -->
</body>

Et voilà ! Il ne reste plus qu'à trouver une façon originale de présenter les boutons.