Mute
const css = `
.toggle-mute { position: relative; display: inline-flex; align-items: center; gap: 14px; color: light-dark(#42454c, #e5e6e9); font-size: 14px; cursor: pointer; user-select: none; }
.toggle-mute__input { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; }
.toggle-mute__scene { position: relative; flex: none; }
.toggle-mute__input:focus-visible + .toggle-mute__scene { outline: 2px solid light-dark(#50535b, #c6c8cd); outline-offset: 4px; border-radius: 8px; }
@keyframes toggle-mute-pulse { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }
.toggle-mute__scene { display: grid; place-items: center; width: 48px; height: 48px; border-radius: 14px; background: light-dark(#f3f4f7, #1a1c1f); box-shadow: inset 0 0 0 1px light-dark(#dbdde1, #2e3035); transition: background-color 0.3s; }
.toggle-mute__scene svg { width: 30px; height: 30px; overflow: visible; }
.toggle-mute__cone { fill: light-dark(#42454c, #e5e6e9); }
.toggle-mute__wave { fill: none; stroke: light-dark(#42454c, #e5e6e9); stroke-width: 1.8; stroke-linecap: round; transform-origin: 9px 15px; transform: scale(0.2); opacity: 0; transition: transform 0.35s cubic-bezier(0.3, 1.5, 0.5, 1), opacity 0.2s; }
.toggle-mute__slash { fill: none; stroke: light-dark(#373a40, #f1f2f3); stroke-width: 2; stroke-linecap: round; stroke-dasharray: 1; stroke-dashoffset: 0; transition: stroke-dashoffset 0.3s 0.15s; }
/* Sound on: the waves spread out, nearest first, and keep a soft pulse; the slash wipes away. */
.toggle-mute__input:checked + .toggle-mute__scene { background: light-dark(#ebedf0, #232529); }
.toggle-mute__input:checked + .toggle-mute__scene .toggle-mute__wave { transform: none; opacity: 1; animation: toggle-mute-pulse 1.6s ease-in-out infinite var(--d); transition-delay: var(--t); }
.toggle-mute__input:checked + .toggle-mute__scene .toggle-mute__slash { stroke-dashoffset: 1; transition-delay: 0s; }
@media (prefers-reduced-motion: reduce) {
.toggle-mute * { transition: none !important; animation: none !important; }
}
`;
export function MuteToggle() {
return (
<>
<style>
{css}
</style>
<label className="toggle-mute">
<input className="toggle-mute__input" type="checkbox" role="switch" defaultChecked />
<span className="toggle-mute__scene" aria-hidden="true">
<svg viewBox="0 0 30 30">
<path className="toggle-mute__cone" d="M3 11h4l6-5v18l-6-5H3Z" />
<path className="toggle-mute__wave" d="M17 11a5 5 0 0 1 0 8" style={{ '--d': '0s', '--t': '0s' } as React.CSSProperties} />
<path className="toggle-mute__wave" d="M20 7.5a10 10 0 0 1 0 15" style={{ '--d': '-0.3s', '--t': '0.06s' } as React.CSSProperties} />
<path className="toggle-mute__wave" d="M23 4a15 15 0 0 1 0 22" style={{ '--d': '-0.6s', '--t': '0.12s' } as React.CSSProperties} />
<path className="toggle-mute__slash" d="M4 4L26 26" pathLength={1} />
</svg>
</span>
Sound
</label>
</>
);
}Markup where the component goes, CSS in your stylesheet. No JavaScript.
<label class="toggle-mute"><input class="toggle-mute__input" type="checkbox" role="switch" checked=""/><span class="toggle-mute__scene" aria-hidden="true"><svg viewBox="0 0 30 30"><path class="toggle-mute__cone" d="M3 11h4l6-5v18l-6-5H3Z"></path><path class="toggle-mute__wave" d="M17 11a5 5 0 0 1 0 8" style="--d:0s;--t:0s"></path><path class="toggle-mute__wave" d="M20 7.5a10 10 0 0 1 0 15" style="--d:-0.3s;--t:0.06s"></path><path class="toggle-mute__wave" d="M23 4a15 15 0 0 1 0 22" style="--d:-0.6s;--t:0.12s"></path><path class="toggle-mute__slash" d="M4 4L26 26" pathLength="1"></path></svg></span>Sound</label>.toggle-mute { position: relative; display: inline-flex; align-items: center; gap: 14px; color: light-dark(#42454c, #e5e6e9); font-size: 14px; cursor: pointer; user-select: none; }
.toggle-mute__input { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; }
.toggle-mute__scene { position: relative; flex: none; }
.toggle-mute__input:focus-visible + .toggle-mute__scene { outline: 2px solid light-dark(#50535b, #c6c8cd); outline-offset: 4px; border-radius: 8px; }
@keyframes toggle-mute-pulse { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }
.toggle-mute__scene { display: grid; place-items: center; width: 48px; height: 48px; border-radius: 14px; background: light-dark(#f3f4f7, #1a1c1f); box-shadow: inset 0 0 0 1px light-dark(#dbdde1, #2e3035); transition: background-color 0.3s; }
.toggle-mute__scene svg { width: 30px; height: 30px; overflow: visible; }
.toggle-mute__cone { fill: light-dark(#42454c, #e5e6e9); }
.toggle-mute__wave { fill: none; stroke: light-dark(#42454c, #e5e6e9); stroke-width: 1.8; stroke-linecap: round; transform-origin: 9px 15px; transform: scale(0.2); opacity: 0; transition: transform 0.35s cubic-bezier(0.3, 1.5, 0.5, 1), opacity 0.2s; }
.toggle-mute__slash { fill: none; stroke: light-dark(#373a40, #f1f2f3); stroke-width: 2; stroke-linecap: round; stroke-dasharray: 1; stroke-dashoffset: 0; transition: stroke-dashoffset 0.3s 0.15s; }
/* Sound on: the waves spread out, nearest first, and keep a soft pulse; the slash wipes away. */
.toggle-mute__input:checked + .toggle-mute__scene { background: light-dark(#ebedf0, #232529); }
.toggle-mute__input:checked + .toggle-mute__scene .toggle-mute__wave { transform: none; opacity: 1; animation: toggle-mute-pulse 1.6s ease-in-out infinite var(--d); transition-delay: var(--t); }
.toggle-mute__input:checked + .toggle-mute__scene .toggle-mute__slash { stroke-dashoffset: 1; transition-delay: 0s; }
@media (prefers-reduced-motion: reduce) {
.toggle-mute * { transition: none !important; animation: none !important; }
}Goal: Create a "Mute" toggle.
Visual behavior: A sound switch: switching on, the sound waves spread from the speaker one after another and keep a soft pulse, and the mute slash wipes away.
Technique:
- A real native checkbox with role="switch" (visually hidden: absolute, 1x1, opacity 0) inside a <label>; every visual is aria-hidden and styled from the input with :checked + sibling selectors.
- Three SVG arcs scale up from the speaker cone with staggered transition delays (--t) and then pulse on a loop with staggered animation delays (--d).
- The slash is a path with pathLength="1" whose dash offset transitions to 1 to wipe it away.
Accessibility: A native checkbox with role="switch" inside a <label>, so Space toggles it and screen readers announce a switch; the visible label names it and :focus-visible on the input outlines the visual. Under prefers-reduced-motion the icon changes instantly with no pulse.
My stack: {{USER_STACK}}
My styling: {{USER_STYLING}}
My constraints: {{USER_CONSTRAINTS}}
My colors: neutral only. Black, white and grays, no hues. Use the color values above as written, or my project's closest neutral tokens.
Return a single self-contained component. No dependencies.Goal: Create a "Mute" toggle.
Visual behavior: A sound switch: switching on, the sound waves spread from the speaker one after another and keep a soft pulse, and the mute slash wipes away.
Technique:
- A real native checkbox with role="switch" (visually hidden: absolute, 1x1, opacity 0) inside a <label>; every visual is aria-hidden and styled from the input with :checked + sibling selectors.
- Three SVG arcs scale up from the speaker cone with staggered transition delays (--t) and then pulse on a loop with staggered animation delays (--d).
- The slash is a path with pathLength="1" whose dash offset transitions to 1 to wipe it away.
Accessibility: A native checkbox with role="switch" inside a <label>, so Space toggles it and screen readers announce a switch; the visible label names it and :focus-visible on the input outlines the visual. Under prefers-reduced-motion the icon changes instantly with no pulse.
My stack: {{USER_STACK}}
My styling: {{USER_STYLING}}
My constraints: {{USER_CONSTRAINTS}}
My colors: use my project's existing color tokens. The color values above are grayscale placeholders. Map each one by its role (surface, text, border, accent, glow) to my theme, and keep their relative lightness so contrast and the effect still read.
Return a single self-contained component. No dependencies.Paste into Claude, ChatGPT or Cursor after filling in {{USER_STACK}}, {{USER_STYLING}} and {{USER_CONSTRAINTS}}. Neutral keeps the greys you see here; Match my project asks the model to map them onto your own colour tokens.
Free to use in any project, commercial included. MIT-0, no attribution required. Licence
About this component
A sound switch: switching on, the sound waves spread from the speaker one after another and keep a soft pulse, and the mute slash wipes away.
- Dependencies
- None
- Difficulty
- Beginner
- Triggers
- click, state change, icon swap, delay stagger
- Techniques
- svg, stroke dashoffset, css transition, css variables
- Reduced motion
- Respected
- Theme
- Light and dark (follows your page's color-scheme)
A native checkbox with role="switch" inside a <label>, so Space toggles it and screen readers announce a switch; the visible label names it and :focus-visible on the input outlines the visual. Under prefers-reduced-motion the icon changes instantly with no pulse.