Bouton clair / sombre
Un bouton clair, sombre ou système, qui respecte le réglage de l'ordinateur tant qu'on ne choisit rien. Le bon exercice pour comprendre les préférences de l'utilisateur.
- Lire prefers-color-scheme avec matchMedia
- Retenir un choix dans localStorage
- Changer de thème avec des variables CSS
Pourquoi un bouton de thème ? Parce que beaucoup de gens règlent leur téléphone en sombre, et qu'un site tout blanc à minuit les éblouit.
La bonne règle : suivre le système par défaut, et laisser l'utilisateur forcer clair ou sombre s'il le veut. Son choix est retenu pour la prochaine visite.
Pourquoi en JavaScript simple ? Le CSS fait presque tout avec des variables. Le JavaScript ne fait que poser un attribut sur la page et retenir le choix.
LES COULEURS EN VARIABLES
Un seul jeu de règles, deux jeux de couleurs.
:root { --fond: #ede8df; --texte: #09090e; } :root[data-theme="sombre"] { --fond: #09090e; --texte: #ede8df; } body { background: var(--fond); color: var(--texte); }LES TROIS BOUTONS
<div role="group" aria-label="Thème"> <button data-t="clair">Clair</button> <button data-t="sombre">Sombre</button> <button data-t="systeme">Système</button> </div>
LE RÉGLAGE DU SYSTÈME
matchMedia lit la préférence, et prévient quand elle change.
const systeme = matchMedia('(prefers-color-scheme: dark)');APPLIQUER
Le choix enregistré gagne. Sans choix, on suit le système.
function appliquer() { const choix = localStorage.getItem('theme') || 'systeme'; const theme = choix === 'systeme' ? (systeme.matches ? 'sombre' : 'clair') : choix; document.documentElement.dataset.theme = theme; document.querySelectorAll('[data-t]').forEach(b => b.setAttribute('aria-pressed', String(b.dataset.t === choix))); }LES ÉCOUTEURS
document.querySelectorAll('[data-t]').forEach(b => b.addEventListener('click', () => { localStorage.setItem('theme', b.dataset.t); appliquer(); })); systeme.addEventListener('change', appliquer); appliquer();
PIÈGE 1 : Le flash blanc au chargement
Mauvais : appliquer le thème dans un script en bas de page. La page s'affiche en clair, puis bascule.
Correct : un petit script dans le <head>, avant le CSS, qui pose data-theme tout de suite.
PIÈGE 2 : Ignorer le système
Mauvais : démarrer toujours en clair et obliger chacun à cliquer.
Correct : prefers-color-scheme par défaut, et le bouton seulement pour changer d'avis.
PIÈGE 3 : Deux feuilles de style
Mauvais : charger un fichier CSS différent pour chaque thème. Chaque bascule recharge et clignote.
Correct : des variables CSS, et un seul attribut qui change.
- Ajoutez color-scheme: light dark dans :root pour que les barres de défilement et les champs suivent aussi.
- Un troisième thème, par exemple contraste élevé : un bloc de variables de plus suffit.
- Une transition douce sur background et color, coupée avec prefers-reduced-motion.
- La meta theme-color peut changer avec le thème pour colorer la barre du navigateur sur mobile.
- Pensez aux images : un logo sombre disparaît sur un fond sombre, prévoyez une version claire avec <picture> et prefers-color-scheme.
Le choix vit dans un contexte (createContext) pour que toute l'application y ait accès.
Un useEffect écoute matchMedia et nettoie son écouteur au démontage.
Le script anti-flash reste en dehors de React, dans le <head> du document.
Chargement des commentaires…