Transitions✨ Premium

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.

CSSIconMorph

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

Programme jour vers programme nuit — thermostat d'une chaudière — Icon Toggle exemple 1

① Programme jour vers programme nuit — thermostat d'une chaudière

QuandLe soir, l'habitant passe du programme jour (soleil, consigne 20 °C) au programme nuit (lune, consigne 17 °C) sur l'écran mural de la chaudière.
PourquoiSeul le bouton change : le soleil tourne et laisse la place à la lune, ce qui confirme le programme sans redessiner l'écran ; le cadran et le chiffre suivent, l'œil sait où regarder.
RéglagesSens inversé : le soleil est l'état de départ (active posé dans le HTML), la lune l'état basculé ; bouton agrandi à 88 px, icônes de 38 px ; libellés « Programme jour · 20 °C » et « Programme nuit · 17 °C » dans data-on / data-off.
Éclairage d'une rue forcé — tableau de bord des services techniques d'une commune — Icon Toggle exemple 2

② Éclairage d'une rue forcé — tableau de bord des services techniques d'une commune

QuandPendant une alerte orage, l'agent force l'allumage de la rue des Cordiers : le soleil (éclairage coupé) pivote et devient une lune (éclairage allumé), dans une liste de quarante rues.
PourquoiLa rotation donne un retour net sur un petit bouton, lisible même au milieu de la liste ; le libellé « Allumé · forcé » à côté confirme en toutes lettres.
RéglagesUn bouton par rangée, réduit à 42 px avec des icônes de 22 px (le quart de tour reste visible) ; la rangée qui bascule en démonstration a un bouton de 62 px (icônes de 32 px) ; libellé placé à gauche du bouton en ligne ; le script de l'effet arme les quatre boutons de la liste sans réglage.
Réveil vers coucher — journal de sommeil d'un nourrisson — Icon Toggle exemple 3

③ Réveil vers coucher — journal de sommeil d'un nourrisson

QuandLe parent, bébé dans les bras, note d'une main le coucher de 19 h 45 : le soleil « réveillé à 7 h 10 » tourne en lune « couché à 19 h 45 ».
PourquoiL'icône qui pivote suffit comme confirmation : un coup d'œil dit ce qui vient d'être enregistré, sans relire la liste.
RéglagesBouton de 86 px en bas d'écran, dans la zone du pouce ; fond brun foncé la nuit, crème doré le jour ; libellé en gras sous le bouton.

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) avec aria-pressed à true quand le soleil est affiché ; les deux icônes sont aria-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 #fbbf24 et gris #94a3b8 sur 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.

Chrome 84+✓ Complet
Firefox 63+✓ Complet
Safari 14.1+✓ Complet
Edge 84+✓ Complet
Mobile iOS 14.5+✓ Complet
Android Chrome 84+✓ Complet

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) :

index.html — structure
<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>
Débloquer le code complet — 9,90 € TTC/mois

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éfautEffet
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

Non : il anime son icône, met à jour 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.
Un tracé SVG ne s'interpole pas d'une forme à une autre sans outil dédié. Deux icônes au même endroit, chacune avec son opacité et sa rotation, donnent un passage continu avec deux transitions CSS. Comme elles sont en position: absolute, le bouton garde sa taille et rien autour ne bouge.
Oui : le script arme chaque .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é.