Elastic Toggle
CSS-only elastic switch: the white knob slides 24 px with a slight overshoot and the rail turns green, on top of a real accessible switch.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Forms has 20 effects, including 2 free. Explore the category →
3 usage examples



How it works
Each row is a <label class="etg-row"> holding the real <input type="checkbox" role="switch">, the drawn rail .etg-track (52 × 28 px, 24 px .etg-knob) and the text. The real checkbox is transparent and sits under the rail, at its size: it gets the click, the finger, the focus and the state, and clicking the text toggles too since everything lives in the label.
When checked, the input:checked + .etg-track selector moves the rail from rgba(255,255,255,.2) to green #10b981 (0.3 s) and pushes the knob 24 px with transform: translateX(24px). The cubic-bezier(.68,-.55,.265,1.55) curve goes beyond the 0-1 range: the knob slightly overshoots its end position then settles back, both ways. That bounce is what makes it elastic.
No JavaScript and no :has(): the rail colour follows the state through the adjacent sibling selector alone. Classes are prefixed etg-; the .fx-0295-root root (dark background, 300 px) is just a display frame to remove in a real page.
Accessibility
- Switch role:
role="switch"on the checkbox makes it announced as "Notifications, switch, on / off" instead of "checkbox". - Accessible name: the "Notifications" text is in the same label as the checkbox, so it is its name; with no visible text, add
aria-labelto the input. - Keyboard: Tab reaches the switch, Space toggles it; an
#a5b4fcring surrounds the rail on keyboard focus (:focus-visible). - Reduced motion: under
prefers-reduced-motion: reduce, the knob and rail change with no slide or bounce. - Colour: the knob's position (left / right) tells the state without relying on green.
Browser compatibility
CSS only (:checked, adjacent sibling selector, transform, out-of-range cubic-bezier). Measured in Chromium 151, Firefox 153 and WebKit 26.5 (Playwright): two independent copies, Tab then Space, touch at 390 px, reduced motion, accessible name read as a switch. The focus ring needs :focus-visible (Chrome 86, Firefox 85, Safari 15.4).
Older browsers: the switch toggles and slides, only the keyboard focus ring is missing.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0295-root">
<label class="etg-row">
<input type="checkbox" role="switch" name="notifications">
<span class="etg-track" aria-hidden="true"><span class="etg-knob"></span></span>
<span class="etg-text">Notifications</span>
</label>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Active colour (CSS — input:checked + .etg-track) |
#10b981 | The checked rail's background; the focus ring stays #a5b4fc. |
Resting colour (CSS — background of .etg-track) |
rgba(255,255,255,.2) | On a light background, switch to a solid grey (#d6d6dc) so the rail stays visible. |
Bounce (CSS — transition of .etg-knob) |
.3s cubic-bezier(.68,-.55,.265,1.55) | ease removes the overshoot; -.8 / 1.8 exaggerates it. |
Size (CSS — .etg-track, .etg-knob, input) |
52 × 28 px, 24 px knob, 24 px travel | Keep travel = rail width − knob − 4 px, and give the input the rail's size. |
Demo frame (CSS — .fx-0295-root) |
min-height 300px, #0a0a0f background | Remove it to place the switch in a page; the text then follows the page's colour. |
Sent name (HTML — name, value) |
notifications = on | In a form, only a switched-on checkbox is sent; add a meaningful value. |
FAQ
flex-direction: row-reverse on .etg-row, plus justify-content: space-between to push it away from the text. Move the invisible input under the rail (left: auto; right: 0).prefers-reduced-motion rule turns the transition off. The switch changes state exactly the same way.