Sound, the way you like it.
Activate studio mode for immersive listening.
Pure CSS toggle switch with a spring-eased knob animation — cubic-bezier(0.34, 1.56, 0.64, 1) overshoot gives a physical feel with zero JavaScript.
This effect is free — the Atmosphere category contains 57 effects total, including 10 free. Effect.Labs has 811 vanilla effects. Explore the category →
Activate studio mode for immersive listening.
The component is built from two nested elements: the track (.snd-toggle, 60 × 32 px, border-radius 16 px) and the knob (.snd-toggle-knob, 26 × 26 px, absolutely positioned 3 px from each edge). Toggling state means adding or removing the .on class on the track — all else is CSS.
The knob animation uses transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1). This is an overshoot easing: the Y coordinate of the second control point (1.56) exceeds 1.0, causing the knob to slide slightly past its target (translateX(28px)) before settling back — that mechanical bounce is what makes this toggle feel physical rather than just sliding. The track background (rgba(255,255,255,0.1) → rgb(99,102,241)) transitions independently with its own 0.3s.
The 28 px knob travel is derived from: track width − knob size − 2 × padding = 60 − 26 − 6 = 28. If you resize the component, recalculate this value or the knob will overshoot or fall short. The label (#sndToggleLabel) and click handling are not included in the provided code: the JS field holds an empty comment indicating a pure-CSS component — click wiring and label updates are the developer's responsibility.
For differentiated sounds (as the effect name implies), the recommended approach is the Web Audio API: create an AudioContext, generate two short OscillatorNodes (high frequency ~880 Hz for ON, low ~220 Hz for OFF) with a fast gain envelope (attack + release in ~40 ms). The IDs sndToggle and sndToggleLabel are the intended entry points for that wiring.
@media (prefers-reduced-motion: reduce) block. Both transitions (background 0.3s, spring transform 0.3s) run unconditionally. Add @media (prefers-reduced-motion: reduce) { .snd-toggle, .snd-toggle-knob { transition: none; } } to honour OS preferences..snd-toggle is a <div> with no role="switch", no aria-checked, and no tabindex. Screen readers do not identify it as an interactive switch. Add role="switch", tabindex="0", and toggle aria-checked="false" / "true" in your click handler.keydown listener for Space or Enter in the provided code. Keyboard-only users cannot toggle the switch without additional JS.var(--text-secondary) depends on the host page theme. WCAG AA (≥ 4.5:1) is not guaranteed by the code as-is — verify in your deployment context.cursor: pointer, a correct signal for a clickable area; the 60 × 32 px hit area meets WCAG 2.5.5 minimum target size for touch.Pure CSS component: transitions, transform, and cubic-bezier have been supported in all modern browsers since 2015. No JavaScript provided, no external dependencies.
Without CSS transition support (very old browsers), the knob jumps instantly to position and the background changes without animation — fully functional, no JS errors.
Copy the three blocks into your page. No dependencies.
<div class="snd-toggle-container">
<div class="snd-toggle" id="sndToggle">
<div class="snd-toggle-knob">
</div>
</div>
<div class="snd-toggle-label" id="sndToggleLabel">OFF</div>
</div>
.snd-toggle-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
.snd-toggle {
width: 60px;
height: 32px;
border-radius: 16px;
background: rgba(255, 255, 255, 0.1);
position: relative;
cursor: pointer;
transition: background 0.3s;
}
.snd-toggle.on {
background: rgb(99, 102, 241);
}
.snd-toggle-knob {
width: 26px;
height: 26px;
border-radius: 50%;
background: rgb(255, 255, 255);
position: absolute;
top: 3px;
left: 3px;
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.snd-toggle.on .snd-toggle-knob {
transform: translateX(28px);
}
.snd-toggle-label {
font-size: 0.8rem;
color: var(--text-secondary);
}
// Effet CSS pur — pas de JavaScript nécessaire
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| .snd-toggle.on { background } | rgb(99,102,241) | Indigo-500 track color in ON state — swap for your primary brand color. |
| .snd-toggle { background } | rgba(255,255,255,0.1) | Semi-transparent track in OFF state. Raise opacity (e.g. 0.25) if contrast is insufficient on a dark background. |
| .snd-toggle { width / height } | 60px / 32px | Track dimensions. Keep ~2:1 ratio; update border-radius (= height/2) and translateX accordingly. |
| .snd-toggle-knob { width / height } | 26px / 26px | Circular knob size. Must remain ≤ height − 6px to stay within the 3 px margin on each side. |
| .snd-toggle.on .snd-toggle-knob { transform } | translateX(28px) | Knob travel in ON state = width − knob − 2 × 3px. Recalculate if you change the dimensions. |
| .snd-toggle-knob { transition: … cubic-bezier } | cubic-bezier(0.34, 1.56, 0.64, 1) | Spring easing with overshoot (1.56 > 1.0). Lower the third parameter toward 1.0 for a linear slide with no bounce. |
| .snd-toggle-knob { transition-duration } | 0.3s | Knob animation duration. 0.15s = fast and snappy, 0.5s = slow and deliberate (update the track transition too). |
The provided code is the CSS foundation: track, knob, and spring animation. Click handling, label updates, and sounds (Web Audio API) are left to the developer to fit any framework. Copy the CSS, add document.getElementById('sndToggle').addEventListener('click', fn), toggle the .on class in fn, and fire your oscillators.
Use the Web Audio API (zero dependencies): create an AudioContext, in the click handler spawn an OscillatorNode at 880 Hz (ON) or 220 Hz (OFF), pipe it through a GainNode with a short envelope (gain 0 → 0.3 in 5 ms, → 0 in 40 ms), then call osc.start() / osc.stop(ctx.currentTime + 0.06). Around 15 lines of JS, fully compatible with autoplay policies since it fires on a user gesture.
Add to the .snd-toggle div: role="switch", tabindex="0", aria-label="Enable sound", and aria-checked="false". In your handler, toggle aria-checked between "true" and "false" alongside the .on class. For keyboard, listen for keydown on Space and Enter to trigger the same handler as the click.