Theme Toggle
Interrupteur soleil / lune qui bascule vraiment le thème : du cadre de démo à toute la page, réglage de l'appareil et mémoire du choix en option.
Mis à jour le
Cet effet fait partie d'Effect.Labs — 840 effets vanilla, une partie gratuite, une partie premium. Utilitaires comprend 43 effets, dont 3 gratuits. Découvrir la catégorie →
3 exemples d'usage



Comment ça marche
L'interrupteur est un vrai <button role="switch">. Au clic, à la touche Entrée ou Espace, aria-checked passe de false à true : la pastille glisse vers la droite avec un léger dépassement (cubic-bezier(.68,-.55,.265,1.55), 0,4 s), le soleil tourne et disparaît, la lune apparaît. true veut dire thème clair.
En même temps, le script pose la classe thm-light sur une cible et la retire au retour en sombre. Sans réglage, la cible est le cadre .thm-frame qui entoure l'interrupteur (il passe de #1a1a2e à #f0f0f5). Avec data-thm-target="html" sur la racine, c'est toute la page qui reçoit la classe : vos propres règles html.thm-light { … } font le reste. Le nom de la classe se change avec data-thm-class.
Au chargement, l'état de départ est sombre, sauf si la cible porte déjà la classe (posée par votre serveur, par exemple). data-thm-start="system" lit prefers-color-scheme et suit les changements du réglage de l'appareil tant que le visiteur n'a rien choisi ; data-thm-start="light" démarre en clair. data-thm-storage="theme" enregistre le choix dans localStorage sous cette clé et le reprend à la visite suivante ; sans cet attribut, rien n'est écrit sur l'appareil.
Chaque .fx-0737-root est indépendant ; deux interrupteurs qui visent la même cible (en-tête et pied de page) restent d'accord. Chaque bascule envoie l'événement fx0737:change (detail.light), et root.fx0737.set(true), toggle(), isLight() pilotent l'interrupteur depuis votre code.
Accessibilité
- Rôle et état : un
<button>avecrole="switch"etaria-checked; un lecteur d'écran annonce « Thème clair, interrupteur, activé ». Le nom vient dearia-label, à traduire ou adapter dans le HTML. Le soleil et la lune sont décoratifs (aria-hidden). - Clavier : Tab pour l'atteindre, Espace ou Entrée pour basculer ; anneau lavande au focus clavier (
:focus-visible). Sur Safari pour Mac, Tab ne s'arrête sur les boutons que si l'option clavier du système est activée (sinon Option + Tab). - Contraste : en clair, le rail
#e0e0e8se détache peu du cadre#f0f0f5; c'est la pastille indigo qui montre l'état. Sur une page très claire, donnez au rail une bordure plus foncée. - Mouvement réduit : sous
prefers-reduced-motion: reduce, plus de glissement, de rotation ni de fondu ; l'état change sur place et la bascule du thème fonctionne pareil.
Compatibilité navigateur
Un bouton, du CSS et 3,5 Ko de JavaScript sans dépendance, commentaires compris (classList, CustomEvent, matchMedia, localStorage). Mesuré dans Chromium 151, Firefox 153 et WebKit 26.5 (Playwright) : code collé deux fois dans une page, chaque copie bascule seule, clavier, toucher à 390 px, mouvement réduit, cible html partagée par deux interrupteurs, choix gardé après rechargement, 0 erreur, aucun nom ajouté sur window.
Sans JavaScript, le bouton s'affiche mais ne bascule rien. Avant Chrome 86, Firefox 85 et Safari 15.4 (pas de :focus-visible), tout marche mais sans anneau de focus. data-thm-start="system" demande prefers-color-scheme (Chrome 76, Firefox 67, Safari 12.1) ; ailleurs, le départ reste sombre.
Le code
Structure HTML à coller dans votre page (le CSS + JS sont inclus avec votre achat) :
<div class="fx-0737-root" data-thm-target="html" data-thm-start="system" data-thm-storage="theme">
<div class="thm-frame">
<button type="button" class="thm-switch" role="switch" aria-checked="false" aria-label="Thème clair">
<span class="thm-knob" aria-hidden="true">
<span class="thm-sun">☀</span>
<span class="thm-moon">☾</span>
</span>
</button>
</div>
</div>
Paiement unique, sans abonnement — le code est à toi pour toujours
Personnaliser
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
Cible (HTML — data-thm-target sur la racine) |
le cadre .thm-frame |
Sélecteur CSS de l'élément qui reçoit la classe : html pour tout le site, #app pour une zone. |
Classe du thème clair (HTML — data-thm-class) |
thm-light |
Présente en clair, absente en sombre. Prenez celle que votre CSS utilise déjà (light, is-light…). |
Départ (HTML — data-thm-start) |
dark |
light démarre en clair ; system lit prefers-color-scheme et suit ses changements jusqu'au premier choix du visiteur. |
Mémoire (HTML — data-thm-storage) |
absent : rien n'est enregistré | Clé localStorage (ex. theme) ; la valeur light ou dark est reprise à la visite suivante et passe avant data-thm-start. |
Nom lu (HTML — aria-label du bouton) |
« Thème clair » | Dites ce que l'état activé montre : « Vue de jour », « Mode clair »… |
Cadre de démo (CSS — --thm-height, --thm-page, --thm-panel, --thm-panel-light, --thm-frame-height) |
300 px · #0a0a0f · #1a1a2e · #f0f0f5 · 120 px |
Mettez 0 et transparent pour poser l'interrupteur seul dans un en-tête. |
Pilotage (JS — root.fx0737 et événement fx0737:change) |
— | set(true) force le clair, toggle() inverse, isLight() lit l'état ; l'événement donne detail.light à chaque bascule. |
FAQ
<head>, avant vos styles : <script>try{if(localStorage.getItem('theme')==='light')document.documentElement.classList.add('thm-light')}catch(e){}</script>, avec la même clé que data-thm-storage et la cible html.:root { --fond: #0f1117; --texte: #e5e7eb } puis html.thm-light { --fond: #fff; --texte: #1f2937 }, et partout background: var(--fond).data-thm-start="system", tant qu'il n'a pas touché l'interrupteur et qu'aucun choix n'est mémorisé. Dès qu'il choisit, son choix passe avant le réglage de l'appareil.