webdevpuneet.com

Elastic Spring Toggle Switch — Bounce Animation Snippet

Elastic Spring Toggle Switch · Buttons · Plain HTML, CSS & JS · Live preview

CategoryButtons

Copy & paste

Source Code

<div class="es-stage">
  <p class="es-hint">Click a switch — the thumb overshoots past its target and wobbles before settling, real spring physics</p>
  <label class="es-row">
    <span>Dark mode</span>
    <button class="es-toggle" id="esToggle1" role="switch" aria-checked="true" data-on="true">
      <span class="es-track"></span>
      <span class="es-thumb"></span>
    </button>
  </label>
  <label class="es-row">
    <span>Auto-updates</span>
    <button class="es-toggle" id="esToggle2" role="switch" aria-checked="false" data-on="false">
      <span class="es-track"></span>
      <span class="es-thumb"></span>
    </button>
  </label>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Thumb position driven by a hand-written spring-damper loop, not a CSS ease transition
Visible overshoot past the target side before settling — genuine elastic motion
Logical on/off state updates instantly and separately from the animated thumb position
aria-checked kept in sync immediately on click for correct accessibility behavior
Mid-animation clicks redirect the spring smoothly using its existing velocity
No animation on initial page load — switches render in their correct starting position instantly
Self-terminating requestAnimationFrame loop stops once the thumb visibly settles
Configurable stiffness, damping, and travel-distance constants
Export as HTML file, React JSX, or React + Tailwind CSS
Mobile (375px), Tablet (768px), Desktop device preview buttons
Live split-pane editor — preview updates as you type

About this UI Snippet

Elastic Spring Toggle Switch — Overshoot Thumb Motion via a Hand-Written Spring Loop

Screenshot of the Elastic Spring Toggle Switch snippet rendered live

A standard toggle switch slides its thumb with a CSS transition: left 0.2s ease — smooth, but mechanically identical every single flip, and incapable of overshooting past its destination. This version replaces that transition with a real spring-damper simulation driving the thumb's transform: translateX, so flipping the switch sends the thumb slightly past the opposite edge before it wobbles back and settles — the same elastic quality as a jelly press button, applied to a binary on/off control instead of a momentary press.

Separating logical state from animated position

The switch's actual on/off state lives in toggle.dataset.on (and is mirrored to aria-checked for accessibility) and changes the instant the user clicks — this is important: any code reading the switch's state, or any screen reader announcing it, sees the correct value immediately, with no lag waiting for an animation to finish. Separately, a target variable tells the spring loop which pixel position the thumb is chasing. Clicking updates both at once, but they're conceptually independent — state is exact and instant, motion is physical and gradual.

The spring loop

Structurally the same integrator used in jelly press button and jelly icon dock hover, but here it drives a single scalar position (pos, in pixels) instead of a 2D scale. Each frame: force = (target - pos) * stiffness, vel = (vel + force) * damping, pos += vel. Because velocity persists and only decays gradually via damping, when the thumb is asked to travel the full 24px distance in one motion, it builds up enough velocity to sail slightly past 24px (or past 0, going the other way) before the restoring force pulls it back — that overshoot-and-correct is the entire elastic effect.

Initializing without animating

On setup, pos and target are both set directly from the switch's starting data-on value with no spring loop triggered — so a page that loads with several switches already in the on position shows them correctly placed instantly, rather than visibly animating in from off on load.

Click handling

Each click flips isOn(), writes the new state to both data-on and aria-checked, updates target to the new side's pixel position, and calls wake() to (re)start the requestAnimationFrame loop if it isn't already running. Clicking rapidly back and forth mid-animation simply changes target again — the spring's existing velocity carries into the new direction, so a fast double-click produces a natural, physically continuous redirect rather than an abrupt jump-cut.

Tuning

Raise stiffness for a faster, more urgent snap; raise damping toward 1 for more visible wobble before settling, or lower it for a quicker, more subdued motion.

Step by step

How to Use

  1. 1
    Click a switchToggle either switch and watch the thumb slide past the far edge slightly before wobbling back into its final resting position.
  2. 2
    Click rapidly back and forthDouble-click quickly — the thumb smoothly redirects mid-motion using its existing velocity, rather than jump-cutting.
  3. 3
    Change the travel distanceAdjust the TRAVEL constant in the JS panel to match a different track width if you resize the .es-toggle in CSS.
  4. 4
    Tune the spring feelChange stiffness (snappiness) and damping (how much it wobbles before settling) at the top of attachElasticToggle.
  5. 5
    Style the track and thumbEdit the .es-track, .es-toggle[data-on="true"] .es-track, and .es-thumb rules in the CSS panel for your own color scheme.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a React + Tailwind version.

Real-world uses

Common Use Cases

Settings and preferences panels
A settings screen with several toggles feels noticeably more crafted when each flip has a small physical snap instead of a flat linear slide.
Mobile-style app settings screens
Mimics the tactile, slightly bouncy toggle feel common in native iOS/Android switches, brought to the web without a UI framework dependency.
Learn 1D spring physics for position, not scale
A focused example of applying the same spring-damper integrator used for scale-based jelly effects to a single positional value instead.
Feature flag and admin toggle panels
Use on an internal admin panel with many boolean settings — the elastic snap gives immediate, satisfying confirmation that a toggle registered.
Onboarding preference selection
A more playful toggle interaction fits an onboarding flow collecting a handful of yes/no preferences from a new user.
Related: Jelly Press Button
See the Jelly Press Button for the same spring-damper technique applied to scale instead of position.

Got questions?

Frequently Asked Questions

The logical state (data-on, aria-checked) must be correct the instant the user clicks — for accessibility, for any code or data binding reading the switch, and for correctness if the user clicks again before the animation finishes. The spring only controls where the thumb visually is on its way to reflecting that state, so the two can update on different schedules without ever conflicting.

The spring loop tracks velocity across frames rather than just interpolating position. When the thumb is asked to travel the full track distance, it builds up enough velocity that the restoring force cannot stop it exactly at the target — it sails slightly past, and only the ongoing force-and-damping cycle pulls it back, producing the wobble-and-settle motion.

The click handler updates target to the new side's pixel position immediately. Because the spring loop always chases whatever target currently is, and because velocity persists across frames, the thumb smoothly redirects using its existing momentum rather than snapping or restarting from scratch.

On initialization, pos and target are both set directly to the starting position (computed from data-on) with no spring loop triggered — this avoids an unwanted animate-in effect on switches that should simply render already in their correct position when the page first loads.

Raise damping (closer to 1) for a longer, more visible wobble with more overshoot bounces before settling; lower it for a quicker, more subdued motion. Raising stiffness makes the whole motion, including the overshoot, happen faster.

Yes. Click "JSX" for a React component. Keep on/off in useState (for rendering and aria-checked) but keep the spring's pos/vel/target/running values in a ref, since they update every animation frame and should not trigger re-renders themselves — write the resulting transform directly to the thumb element via a ref.