Icon Toggle
Soleil et lune superposés au centre d'un bouton rond de 72 px : au clic, l'un pivote de −90° à 0° en grandissant, l'autre part à 90° en rétrécissant, en 0,5 s, et le libellé dessous change de texte. C'est la micro-interaction de l'interrupteur, pas la bascule de la page ; 0,6 Ko de JavaScript, fondu de 150 ms sans rotation si l'utilisateur réduit les animations.
Mis à jour le
Cet effet fait partie d'Effect.Labs — 840 effets vanilla, une partie gratuite, une partie premium. Transitions comprend 52 effets, dont 9 gratuits. Découvrir la catégorie →
3 exemples d'usage



Comment ça marche
Le bouton .th-icon-btn est un vrai <button> rond de 72 px, fond blanc à 5 % et bordure blanche à 10 % de 2 px. Il contient deux icônes SVG de 32 px en trait seul (fill: none, épaisseur 1,5) : .th-icon-sun, en #fbbf24, et .th-icon-moon, en #94a3b8. Les deux sont en position: absolute au même endroit, centrées par le bouton en flex : l'une remplace l'autre sans que rien ne se décale. Au repos (thème sombre), la lune est droite et pleine (rotate(0deg) scale(1), opacité 1) ; le soleil attend tourné de −90°, réduit de moitié et invisible.
La classe .active inverse les rôles : le soleil vient à rotate(0deg) scale(1) et opacité 1, la lune part à rotate(90deg) scale(.5) et opacité 0, les deux avec transition: opacity .5s, transform .5s sur la courbe cubic-bezier(.4, 0, .2, 1). Les deux icônes tournent donc dans le même sens, celui des aiguilles d'une montre, comme un disque qui pivote d'un quart de tour : l'une s'efface en tournant pendant que l'autre arrive. Le bouton prend un fond ambre à 10 % et une bordure ambre à 30 % (background-color .5s, border-color .5s). Mesuré 300 ms après le clic : soleil à 0,89 d'opacité dans Chromium 151 et WebKit 26, 0,88 dans Firefox 153, tourné d'environ −10° ; tout est à sa place à 0,5 s, et le second clic rejoue le quart de tour en sens inverse.
Le script (0,6 Ko) arme chaque .th-icon-btn de la page, sans id ni fonction globale : au clic, il bascule .active, met aria-pressed à jour et écrit dans le .th-icon-label voisin (dans la même .th-icon-scene) le texte de son attribut data-on ou data-off — « Mode clair » / « Mode sombre » ici. Les libellés vivent donc dans le HTML, traduisibles sans toucher au script. L'effet anime le bouton et son libellé, et rien d'autre : il ne change pas le thème de la page, ne lit pas prefers-color-scheme et ne mémorise rien. Branchez votre propre changement de thème sur le même clic (ou lisez aria-pressed).
Accessibilité
- Bouton : un
<button type="button">nommé « Mode clair » (aria-label, le nom ne change pas) avecaria-pressedàtruequand le soleil est affiché ; les deux icônes sontaria-hidden. Mesuré : atteint à la touche Tab dans Chromium, basculé par Entrée et Espace dans Chromium, Firefox et WebKit ; contour violet de 2 px au focus clavier. - Mouvement réduit : sous
prefers-reduced-motion: reduce, plus de rotation ni d'échelle (transform: none), les deux icônes se relaient en fondu d'opacité de 150 ms. Mesuré : soleil à 0,39 (Chromium) et 0,33 (Firefox) d'opacité 60 ms après le clic, 1 à 300 ms, sans transformation. - Contraste : libellé à 50 % de blanc sur
#0a0a0fà 5,3:1 ; les icônes, éléments graphiques, dépassent 3:1 sur le fond du bouton (ambre#fbbf24et gris#94a3b8sur noir). Le libellé texte double l'icône : l'état ne repose pas sur la seule forme soleil ou lune.
Compatibilité navigateur
CSS (transitions de transform et d'opacity, :focus-visible) + 0,6 Ko de JavaScript (closest(), attributs data-), aucune dépendance. Mesuré dans Chromium 151, Firefox 153 et WebKit 26 : mêmes valeurs à 60 et 300 ms à quelques centièmes près, même retour, libellés français et anglais. Les transitions de transform sont universelles ; closest() existe depuis Chrome 41, Firefox 35 et Safari 6 ; :focus-visible depuis Chrome 86, Firefox 85 et Safari 15.4 (avant, le contour de focus par défaut reste). Le plancher vient de gap dans des boîtes flex : Chrome et Edge 84, Firefox 63, Safari 14.1 (iOS 14.5) ; avant, les éléments se touchent.
Sans JavaScript, le bouton reste sur la lune avec le libellé « Mode sombre » et ne réagit pas ; si votre page change de thème côté serveur, posez active et aria-pressed="true" dans le HTML pour afficher directement le soleil.
Le code
Structure HTML à coller dans votre page (le CSS + JS sont inclus avec Premium) :
<div class="th-icon-root">
<div class="th-icon-scene">
<button class="th-icon-btn" type="button" aria-pressed="false" aria-label="Mode clair">
<span class="th-icon-sun" aria-hidden="true"><svg>…</svg></span>
<span class="th-icon-moon" aria-hidden="true"><svg>…</svg></span>
</button>
<span class="th-icon-label" data-on="Mode clair" data-off="Mode sombre">Mode sombre</span>
</div>
</div>
Le code complet HTML + CSS + JS de Icon Toggle, prêt à coller — et les 840 effets. Ou 299 € TTC une fois, accès à vie.
Personnaliser
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
Durée et courbe (CSS — transition de .th-icon-sun / .th-icon-moon) |
.5s cubic-bezier(.4, 0, .2, 1) | 0,35 s pour un interrupteur nerveux ; au-delà de 0,7 s, l'utilisateur attend la fin avant de reprendre. |
Angle et échelle de sortie (CSS — rotate(-90deg) scale(.5) et rotate(90deg) scale(.5)) |
quart de tour, moitié de taille | 180° donne une rotation plus spectaculaire ; scale(0) fait disparaître l'icône au centre. Gardez des signes opposés pour que les deux tournent dans le même sens. |
Taille (CSS — .th-icon-btn et svg) |
72 px et 32 px | Gardez l'icône à environ 45 % du bouton : 42 / 22 px dans une liste, 88 / 38 px pour un écran mural. |
Libellés (HTML — data-on et data-off du .th-icon-label) |
« Mode clair » / « Mode sombre » | Mettez-y l'état réel (« Chauffage nuit · 17 °C ») ; supprimez le .th-icon-label si le bouton est seul, le script s'en passe. Adaptez aussi aria-label. |
Couleurs (CSS — stroke des icônes, fond et bordure de .th-icon-btn.active) |
soleil #fbbf24, lune #94a3b8 |
Sur fond clair, foncez les deux traits (#d97706, #475569) pour garder 3:1. |
Mouvement réduit (CSS — bloc @media (prefers-reduced-motion: reduce)) |
fondu de 150 ms, sans rotation | transition: none pour un échange instantané. |
FAQ
aria-pressed et son libellé, rien d'autre. Pour changer le thème, ajoutez dans le même clic votre propre code, par exemple document.documentElement.classList.toggle('light', btn.classList.contains('active')), ou combinez-le avec une autre bascule de cette catégorie. L'effet ne lit pas non plus prefers-color-scheme et ne mémorise rien : si la page démarre en clair, posez active et aria-pressed="true" dans le HTML.position: absolute, le bouton garde sa taille et rien autour ne bouge..th-icon-btn trouvé et cherche le libellé dans la .th-icon-scene qui l'entoure, sans id. Une liste de quarante rues avec un bouton chacune fonctionne telle quelle ; gardez une .th-icon-scene par paire bouton-libellé.