
The send button
Buttons, Transitions, SVG
NewFree
A light and dark theme switch, in the minimal style: the knob is a moon that changes phase as it runs on a spring, and the same terminator, as big as the page, sweeps across it from the knob and brings the night or the day.
A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React and Vue tabs hold the same demo as a ready component, which switches its animations off when it leaves the page.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>The dark theme switch</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage" data-stage>
<!-- the night: a layer the colour of the other theme, opened by a terminator (the edge between light and shadow)
that sweeps across the page from the knob, the way the shadow sweeps across the moon -->
<div class="sweep" aria-hidden="true" data-sweep></div>
<svg class="edge" aria-hidden="true" focusable="false" data-edge><ellipse data-edge-line></ellipse></svg>
<div class="theme" data-theme>
<span class="theme__name" id="theme-name">Appearance</span>
<div class="theme__row">
<button class="switch" type="button" role="switch" aria-checked="false" aria-labelledby="theme-name theme-word" data-switch data-autoclic>
<svg class="switch__art" viewBox="0 0 104 56" aria-hidden="true" focusable="false">
<rect class="switch__track" x="1" y="1" width="102" height="54" rx="27"></rect>
<g data-knob>
<circle class="switch__rim" cx="0" cy="0" r="19"></circle>
<path class="switch__lit" data-lit></path>
</g>
</svg>
</button>
<span class="theme__word" id="theme-word" aria-hidden="true">
<span class="theme__w" data-word-light>Light</span>
<span class="theme__w" data-word-dark>Dark</span>
</span>
</div>
</div>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="script.js"></script>
</body>
</html>Change values, colours and text with the controls and watch the animation respond live. Or write in the code yourself. Then download the folder with your changes.
14 values to change
With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:
Take “The dark theme switch” from Animorio and fit it into my siteThe knob is a thin circle and a path, the lit part, rebuilt on every frame from the lit fraction f: half a circle on the right (the lit limb) and half an ellipse |2f − 1| times the radius wide (the terminator), bulging left when f is over a half and right when it is under. So from 1 (full) to 0.16 (crescent) the shadow crosses it as in the real phases.
A single object of numbers (x, phase, wave, press) animated by a GSAP timeline: x with back.out(0.9), which goes past by about a pixel and comes back; the phase with power2.inOut over the same time. The knob's group moves with a transform written on every update of the timeline.
The night is a layer in the other theme's colour, cut by a clip-path ellipse worked out in pixels on every frame from the knob's real centre: LENS (3) times as tall as the distance to the farthest edge, as wide as it needs to cover the farthest corner (dx over the square root of 1 − (dy/ry)²) times the progress. At first it is a vertical line, then it opens like a lens; an SVG ellipse with the same centre and radii draws the edge in orange. At the end the page's colours move to the new theme and the layer goes back to zero.
The colours are CSS variables: the page uses those on :root, the switch its own, which GSAP brings to the new colours as soon as the edge passes it. The two words sit on top of each other: the leaving one goes to wdth 62 and wght 300 and fades, the arriving one starts at wdth 125 and wght 260 and settles with back.out.
It is a button with role="switch" and aria-checked, labelled by the two words; Space and Enter turn it like a click, and focus shows as an orange ring. The first state comes from prefers-color-scheme and the page also changes color-scheme. A click halfway brings the running turn straight to its end and starts the new one.
| Name | File | Default | What it changes |
|---|---|---|---|
KNOB_TIME | script.js | 0.62 | How long the knob takes to cross, in seconds. |
SPRING | script.js | 0.9 | How far the knob goes past its end (0 not at all; 0.9 is about a pixel). |
WAVE_TIME | script.js | 0.95 | How long the terminator takes to cross the page, in seconds. |
DARK_PHASE | script.js | 0.16 | How much of the moon is lit in the dark theme (1 is a full moon). |
LENS | script.js | 3 | How tall the sweeping lens is: taller, a straighter edge. |
PEEK | script.js | 3 | How far the knob leans to the other end under the mouse, in pixels. |
For the theme switch in any site or app: an app's settings, a blog's header, a portfolio's footer, a dashboard's panel. Wherever whoever turns it should see at once what happens.
The same with a tap: the switch stays 104 by 56 and the word gets a little smaller; the terminator crosses the narrow screen from where the knob is.
No run and no lens: the knob and the moon are already in place, the page and the switch move to the other theme in a short fade and the words swap with a fade.
GSAP 3.15.0, Archivo (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 4.9 KB; the libraries: GSAP 28 KB.
Tested on 21 October 2026
Use it in your own projects and in your clients’ projects. Do not resell it or redistribute it as is. GSAP stays under its own Standard license. Full license.
With your account’s email and password.
or with your email
You’re signed in as .
With your account, you can download the zip of every animation and every pack, for free.