Forms✨ Premium

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.

CSSCubic-bezierToggle

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

Map available offline in a hiking app — Elastic Toggle example 1

① Map available offline in a hiking app

WhenBefore setting off on the three waterfalls loop, the hiker turns on "Available offline" with a tap: the knob shoots right with its little bounce, the rail turns green and the line below reads "Downloaded · 18 MB".
WhyA single setting, applied at once and that really matters once there is no signal: the switch says "done" plainly and the hiker can leave without looking for a Save button.
SettingsSwitch moved to the right by the scene (flex-direction: row-reverse, input moved under the rail), resting rail #d6d3c8, active rail #2f8a4a. The "Downloaded" note comes from the scene's CSS (:has(input:checked)); in the capture, a click outside the switch is relayed to the real switch.
Express delivery in an order summary — Elastic Toggle example 2

② Express delivery in an order summary

WhenBefore paying, the customer turns on "Express — next day": the row takes a green tint and the total goes from €62.40 to €67.30.
WhyOne switch for an option that costs money: the customer sees the option turned on and its price at the same time, with no dropdown to open.
SettingsSingle switch, resting rail #d6cfc5 on a cream background. The total and the row tint are changed by the scene on the change event.
Tiles of a smart-home dashboard — Elastic Toggle example 3

③ Tiles of a smart-home dashboard

WhenEach room is a tile; a click anywhere on "Living room" toggles its switch, the bulb lights up and the tile takes a warm glow. From the keyboard, Tab moves from room to room and Space turns the light on.
WhyThe whole tile is the target: the large label allows wide aiming by finger or mouse, and the switch at the bottom of each tile shows the state of the whole grid at a glance.
SettingsOne tile = one label.etg-row in reversed column (text on top, rail at the bottom), active rail #f59e0b. The tile glow comes from the scene's CSS (:has(input:checked)).

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-label to the input.
  • Keyboard: Tab reaches the switch, Space toggles it; an #a5b4fc ring 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).

Chrome 86+✓ Full (measured)
Firefox 85+✓ Full (measured)
Safari 15.4+✓ Full (measured in WebKit)
Edge 86+✓ Full (Chromium engine)
Mobile Safari 15.4+✓ WebKit engine, not checked on a device
Android Chrome 86+✓ Full (touch measured at 390 px)

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

index.html — structure
<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 / propertyDefaultEffect
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

The switch for a setting that applies at once (notifications, a light); the checkbox for a choice confirmed later by a button (terms, options of an order still to be confirmed).
Yes, without touching the HTML: 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).
Their system asks for less motion: the prefers-reduced-motion rule turns the transition off. The switch changes state exactly the same way.