Liquid✨ Premium

Toggle Morph

A toggle whose knob, as it slides, stretches from 28 to 44 px and becomes a rounded rectangle halfway before turning round again, like a stretching drop, with a bounce, a shift from violet to pink and an updated state label. A real switch as far as screen readers are concerned (a button with the switch role, checked or not), usable from the keyboard; the knob changes sides without stretching when the user reduces motion.

CSSJSUI

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Liquid has 31 effects, including 6 free. Explore the category →

3 usage examples

Available / on break — a taxi driver's app — Toggle Morph example 1

① Available / on break — a taxi driver's app

WhenAt the head of the station rank, the driver's screen shows a large "Available for a ride" toggle; when on, the line below gives their place in the queue.
WhyThe knob that stretches as it slides makes the switch visible from the corner of the eye; an ordinary toggle changes sides without being noticed.
SettingsToggle enlarged with transform: scale(1.6); gray knob at rest, green #4ade80 → #16a34a once on, green track at 30%; data-on="Available", data-off="On break".
Coop door — the app of an automatic chicken door — Toggle Morph example 2

② Coop door — the app of an automatic chicken door

When"Door: closed"; at sunset, the farmer flips it to "open" from her phone to let a late hen back in, and the door in the drawing slides up.
WhyThe stretch conveys the gesture of sliding a hatch, for a physical action done remotely.
SettingsScale 1.3; brown knob #a16207 → #78350f at rest, orange #fb923c → #ea580c once open; light brown track; the page reads aria-checked to raise the door in the drawing.
Bedroom night light — a baby monitor app — Toggle Morph example 3

③ Bedroom night light — a baby monitor app

WhenA very dark night screen at 3 am: a "Night light" toggle and the "on" or "off" label, with the room's temperature and sound level above.
WhyA short, gentle movement (0.5 s) on a dark background, with no flash: you switch the night light without dazzling yourself.
SettingsScale 1.3; slate knob at rest, amber #fcd34d → #f59e0b when on, amber track at 18%; warm glow added by the page when aria-checked becomes true.

How it works

The .toggle-morph track is an 80 × 40 px <button role="switch"> (white background at 10%, 2 px border, 20 px corners); the 28 px .toggle-morph-knob sits 4 px from its left edge. On click, the script toggles .active: the knob slides to 44 px with transition: .5s cubic-bezier(.68, -.55, .27, 1.55), which backs it up a little then overshoots its spot, and its gradient shifts from violet to pink; the track takes a violet tint. At the same time, the script restarts the .morphing class, which plays the fx0410-knob animation over 0.5 s: from 30% to 60% of the way, the knob is 44 px wide with 10 px corners, then it becomes a 28 px circle again on arrival.

Two movements overlap: the position, as a transition, and the shape, as an animation. That stretching shape during the trip is what sets the effect apart from an ordinary toggle: the eye sees matter moving rather than a dot changing sides. So that a quick double click restarts the stretch instead of letting it finish, the script removes the class, forces the browser to register it (void knob.offsetWidth), then adds it back. The class is removed when the animation ends (animationend).

The state lives in aria-checked (true or false), which screen readers announce as "on" or "off". The visible label under the track comes from the button's data-on and data-off attributes ("Enabled" / "Disabled"); it is hidden from screen readers, which already have the state. The button's name, aria-label="Toggle", should be replaced with what it controls: "Available for a ride", "Night light". The script walks through every .fx-0410-root .toggle-morph on the page.

Accessibility

  • Keyboard: measured in Chromium 151 and Firefox 153, Tab reaches the toggle, Enter or Space flips it, and aria-checked follows. 2 px pink focus ring, 3 px offset, for keyboard users only. In Safari, Tab only stops on buttons if the keyboard setting asks for it (or with Option+Tab).
  • Screen readers: announced as "Toggle, switch, off" then "on". Give it a name that says what it controls, without the state in it ("Night light", not "Turn on night light").
  • Reduced motion: under prefers-reduced-motion: reduce, the knob loses its position transition and the script does not add .morphing: it changes sides at once, without stretching. Measured in all three engines: no in-between frame for the knob.
  • Color: the state also reads from the knob's position and from the label, not only from the change to pink. The white label at 50% on a dark background stays above 4.5:1.

Browser compatibility

CSS (transitions, @keyframes) + 1.1 KB of JavaScript, no dependencies. Measured frame by frame in Chromium 151, Firefox 153 and WebKit 26: same path, same halfway stretch, same stop under reduced motion. role="switch" is read by VoiceOver, NVDA and TalkBack; an older reader announces it as a checkbox, with the same state.

Chrome 60+✓ Full
Firefox 60+✓ Full
Safari 12+✓ Full (Tab depends on the keyboard setting)
Edge 79+✓ Full
Mobile iOS 12+✓ Full (touch)
Android Chrome 60+✓ Full (touch)

Without JavaScript, the toggle stays off and does not flip: if the setting matters, back it with a real checkbox in a form. Without CSS animations, the knob changes sides without stretching.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0410-root">
  <div class="toggle-morph-container">
    <button type="button" class="toggle-morph" role="switch" aria-checked="false"
            aria-label="Toggle" data-on="Enabled" data-off="Disabled">
      <span class="toggle-morph-knob"></span>
    </button>
    <span class="toggle-morph-state" aria-hidden="true">Disabled</span>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Accessible name (HTML — aria-label) "Toggle" Name what the toggle controls: "Night light", "SMS notifications". Or link it to visible text with aria-labelledby.
State labels (HTML — data-on / data-off) "Enabled" / "Disabled" The setting's own words: "Open" / "Closed", "Available" / "On break". The script rereads them on every click. Put the starting label in .toggle-morph-state.
Stretch (CSS — @keyframes fx0410-knob) 44 px wide with 10 px corners from 30% to 60% of the way 52 px gives a longer drop; do not exceed the track width minus 8 px. 30%, 60% sets when it stretches: 20%, 70% stretches it earlier and for longer.
Bounce and duration (CSS — the knob's transition: .5s cubic-bezier(.68, -.55, .27, 1.55), the .5s of .morphing) 0.5 s, back-up then overshoot Keep the same duration on both, or the knob stretches after it has arrived. .35s ease gives a snappier toggle, without bounce.
Colors (CSS — track, knob and .active rules) violet knob #a855f7 → #7c3aed, then pink #ec4899 → #a855f7 A neutral color at rest and a strong color once on make the state readable at a glance, like the taxi's gray then green.

FAQ

Listen to its click and read aria-checked: toggle.addEventListener('click', function () { var on = toggle.getAttribute('aria-checked') === 'true'; /* save on */ });. Put this code after the effect's script, which updates the state first. To start switched on, put class="toggle-morph active", aria-checked="true" and the data-on label in the HTML.
A switch says "on / off" and acts right away; a checkbox is ticked and then submitted with a form. role="switch" on a <button> gives both: the button's native keyboard behavior and the "on / off" announcement. If your setting belongs to a form sent all at once, a real styled <input type="checkbox"> remains the best choice.
Because aria-checked already announces "on" or "off". Leaving the label readable too would state it twice, in different words ("Available", then "on"). The label is for the eyes; the spoken state comes from the attribute.