Gérer le thème foncé / clair

Résumé Support

Sur les systèmes d'exploitation modernes, les utilisateurs peuvent basculer entre un thème clair et un thème sombre selon leurs préférences. La bonne nouvelle, c'est qu'on peut détecter cette préférence directement en CSS pour adapter les couleurs de notre site automatiquement. Il y a 2 approches qui permettent de prendre en compte cette préférence : la media query prefers-color-scheme et la fonction light-dark().

La media query prefers-color-scheme

La première approche, et la plus connue, c'est d'utiliser une media query. Comme pour le responsive, on va pouvoir cibler la préférence de l'utilisateur :

@media (prefers-color-scheme: dark) { :root { --color-background: #1a1a1a; --color-foreground: #f5f5f5; } }

L'idée, c'est de définir ses couleurs de base via des variables CSS sur :root, puis de les redéfinir à l'intérieur de cette media query pour le thème sombre. On peut aussi utiliser l'imbrication CSS pour cibler un composant et gérer l'apparence alternative au cas par cas:

.btn { background: var(--color-primary); @media (prefers-color-scheme: dark) { background: var(--color-primary-dark); } }

Tester via l'inspecteur

Si vous êtes sur chrome vous avez la possibilité d'alterner entre thème sombre et clair directement depuis l'inspecteur.

  • On fait Ctrl+Shift+P pour ouvrir la palette de commandes
  • On tape "light" ou "dark" pour trouver la commande "Emulate CSS prefers-color-scheme".

Attention cependant : cette simulation s'arrête dès que vous fermez l'inspecteur.

La fonction light-dark()

La seconde approche est plus récente et est plus pratique si on travaille déjà avec des variables CSS. Le principe est de définir les couleurs à l'aide de la fonction light-dark() qui accepte deux valeurs : d'abord la couleur claire, puis la couleur foncée.

:root { color-scheme: light dark; --color-background: light-dark(#ffffff, #1a1a1a); --color-foreground: light-dark(#0a0a0a, #f5f5f5); --color-border: light-dark(#e5e5e5, #383838); }

La propriété color-scheme: light dark est indispensable ici : c'est elle qui indique au navigateur que le site supporte les deux thèmes et qui permet à light-dark() de fonctionner correctement.

L'avantage de cette approche c'est que toute la logique de thème est centralisée au niveau des variables. Plus besoin de media query séparée : chaque variable embarque directement ses deux variantes.

Si on veut supporter uniquement un des deux thèmes, on peut le préciser avec only :

:root { color-scheme: only light; /* Force le thème clair quoi qu'il arrive */ }

Ce qui est intéréssant avec cette propriété est que l'on peut forcer un élément à adopter un thème de couleur spécifiquement en forcant l'adoption du thème clair ou foncé :

.card-dark { color-scheme: only dark; color: var( --color-foreground ); /* On doit définir la couleur pour ne pas hériter la valeur du parent */ background: var(--color-background); }

Attention en revanche, définir color-scheme ne suffit pas à changer les couleurs héritées du parent. Si une propriété comme color n'est pas redéfinie localement via une variable, elle sera héritée du parent et ne changera pas. Il faut donc bien s'assurer d'utiliser ses variables CSS pour toutes les couleurs que l'on veut voir s'adapter.

Permettre à l'utilisateur de choisir

La dernière pièce du puzzle, c'est de laisser l'utilisateur forcer un thème indépendamment de ses préférences système. L'approche classique consiste à gérer une classe sur l'élément <html> :

.dark { color-scheme: only dark; } .light { color-scheme: only light; }

En JavaScript, un bouton peut alors permettre de basculer entre les deux :

const toggle = document.querySelector("#theme-toggle"); toggle.addEventListener("click", () => { document.documentElement.classList.toggle("dark"); });

Si aucune classe n'est présente sur <html>, le site respecte automatiquement la préférence système. Si la classe .dark ou .light est ajoutée, elle prend le dessus. C'est exactement ce type de fonctionnement que l'on retrouve sur de nombreux sites, dont Grafikart.