Live: try it with the mouse and the keyboard (Tab, arrows, Escape).

Toggle switch UI component for websites and web apps, shown in Fernway, a fictional productivity product. Shows a list of on/off settings with labels and hints, switches on and off, a disabled switch, and every state (default, hover, focus-visible, on, disabled). Playful style: vivid purple, Fredoka headings with Nunito, pill buttons, rounded cards and soft shadows. Comes with copyable HTML, CSS and a few lines of plain JavaScript (keyboard support and ARIA per the WAI-ARIA Authoring Practices), a live demo, and a DESIGN.md with light and dark colour tokens.
#faf8ff · surface #ffffff · text #191522 · muted #666073 · primary #7b34cb · accent #9f5700#120f19 · surface #201b28 · text #edebf3 · muted #b3aebf · primary #b98eff · accent #ff9420Paste the HTML where the component goes, the CSS into your stylesheet and the script before </body>. The tokens block holds the light and dark colours; swap in your own.
<ul class="mgd-switches" aria-label="Notification settings">
<li><span><span class="mgd-switches__label" id="sw-digest">Weekly email digest</span><span class="mgd-switches__hint" id="sw-digest-hint">A Monday summary of what changed in your projects.</span></span>
<button class="mgd-switch" type="button" role="switch" aria-checked="true" aria-labelledby="sw-digest" aria-describedby="sw-digest-hint"><span class="mgd-switch__thumb"></span></button></li>
<li><span><span class="mgd-switches__label" id="sw-desktop">Desktop notifications</span><span class="mgd-switches__hint" id="sw-desktop-hint">Pop-ups while Fernway is open in a browser tab.</span></span>
<button class="mgd-switch" type="button" role="switch" aria-checked="false" aria-labelledby="sw-desktop" aria-describedby="sw-desktop-hint"><span class="mgd-switch__thumb"></span></button></li>
<li><span><span class="mgd-switches__label" id="sw-mentions">Only when I'm mentioned</span><span class="mgd-switches__hint" id="sw-mentions-hint">Mute everything except @mentions and direct replies.</span></span>
<button class="mgd-switch" type="button" role="switch" aria-checked="true" aria-labelledby="sw-mentions" aria-describedby="sw-mentions-hint"><span class="mgd-switch__thumb"></span></button></li>
<li><span><span class="mgd-switches__label" id="sw-sms">Text message alerts</span><span class="mgd-switches__hint" id="sw-sms-hint">Add a phone number in your profile first.</span></span>
<button class="mgd-switch" type="button" role="switch" aria-checked="false" aria-labelledby="sw-sms" aria-describedby="sw-sms-hint" disabled><span class="mgd-switch__thumb"></span></button></li>
</ul>/* Tokens (light + dark) */
:root{--bg:#faf8ff;--surface:#ffffff;--border:#dbd5e7;--text:#191522;--muted:#666073;--primary:#7b34cb;--on-primary:#ffffff;--accent:#9f5700;--font-h:"Fredoka",Georgia,serif;--font-b:"Nunito",system-ui,sans-serif;--hw:600;
--c-bw:2px;--c-line:transparent;--c-r:14px;--c-pill:999px;--c-sh:0 4px 14px rgb(0 0 0 / .10);--c-pop:0 16px 40px rgb(0 0 0 / .18);--c-tt:none;--c-ls:0;--c-fw:700;--c-fs:14px;--c-ring:3px solid var(--accent);--c-ind:4px;color-scheme:light}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--bg:#120f19;--surface:#201b28;--border:#36313f;--text:#edebf3;--muted:#b3aebf;--primary:#b98eff;--on-primary:#111111;--accent:#ff9420;color-scheme:dark}
}
:root[data-theme="dark"]{--bg:#120f19;--surface:#201b28;--border:#36313f;--text:#edebf3;--muted:#b3aebf;--primary:#b98eff;--on-primary:#111111;--accent:#ff9420;color-scheme:dark}
/* Component */
.mgd-sr{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.mgd-switches{list-style:none;margin:0;padding:0;max-width:640px;display:grid;gap:10px}
.mgd-switches li{display:flex;align-items:center;justify-content:space-between;gap:20px;padding:16px 18px;background:var(--surface);border-radius:var(--c-r);box-shadow:var(--c-sh)}
.mgd-switches__label{display:block;font:600 15px/1.35 var(--font-b);text-transform:var(--c-tt);letter-spacing:var(--c-ls)}
.mgd-switches__hint{display:block;margin-top:2px;color:var(--muted);font-size:13px}
.mgd-switch{flex:none;position:relative;width:48px;height:28px;padding:0;border:1.5px solid var(--muted);border-radius:999px;background:color-mix(in oklab,var(--muted) 22%,var(--bg));cursor:pointer;transition:background-color .14s,border-color .14s}
.mgd-switch__thumb{position:absolute;top:50%;left:3px;width:20px;height:20px;margin-top:-10px;border-radius:50%;background:var(--muted);box-shadow:0 1px 3px rgb(0 0 0 / .25);transition:transform .14s,background-color .14s}
.mgd-switch:hover,.mgd-switch.is-hover{background:color-mix(in oklab,var(--muted) 34%,var(--bg))}
.mgd-switch:focus-visible,.mgd-switch.is-focus{outline:var(--c-ring);outline-offset:3px}
.mgd-switch[aria-checked="true"],.mgd-switch.is-on{background:var(--primary);border-color:var(--primary)}
.mgd-switch[aria-checked="true"] .mgd-switch__thumb,.mgd-switch.is-on .mgd-switch__thumb{transform:translateX(20px);background:var(--on-primary)}
.mgd-switch:disabled,.mgd-switch.is-off{opacity:.45;cursor:not-allowed}
@media (forced-colors:active){.mgd-switch[aria-checked="true"] .mgd-switch__thumb{background:Highlight}}
@media (prefers-reduced-motion:reduce){.mgd-switch,.mgd-switch *{transition:none!important;animation-duration:2.4s!important}}document.querySelectorAll(".mgd-switch").forEach(function (sw) {
sw.addEventListener("click", function () { sw.setAttribute("aria-checked", sw.getAttribute("aria-checked") !== "true"); });
});## Component: Toggle switch
On and off settings that apply straight away, each with a visible label and a hint.
- **Pattern:** WAI-ARIA Authoring Practices, Switch (https://www.w3.org/WAI/ARIA/apg/patterns/switch/)
- **Roles and attributes:** Each switch is a <button role="switch"> with aria-checked, named by its visible label (aria-labelledby) and described by its hint (aria-describedby); a disabled switch uses the disabled attribute.
- **States drawn:** default, hover, focus-visible, selected, disabled
- **Focus:** a visible `:focus-visible` outline (3px solid accent), never removed
- **Style knobs (playful):** border 2px, corners 14px, shadow `0 4px 14px rgb(0 0 0 / .10)`, labels sentence case
- **Motion:** transitions are short (≤160ms) and switched off under `prefers-reduced-motion: reduce`
- **Code:** HTML + CSS + a few lines of plain JavaScript (no libraries); colours come only from the tokens above, so light and dark both work
### Keyboard
- **Tab:** move between switches
- **Space / Enter:** turn the focused switch on or off
### Licence
Original Made Good Designs code: free to use and adapt in any project, including commercial ones, with no credit needed. Fonts are Google Fonts under their own licences.Add the Made Good Designs library to your assistant once. Then ask for UI references, brand palettes, font pairings and design tokens in any chat.
https://madegooddesigns.com/inspiration/mcp?ref=ui-toggle-switch-uiSettings → Connectors → Add custom connector → paste the URL.
Settings → Apps & Connectors → Advanced → Developer mode → Create → paste the URL.
One click, then confirm in Cursor.
One click, then confirm in VS Code (Copilot agent mode).
claude mcp add --transport http made-good-designs https://madegooddesigns.com/inspiration/mcp?ref=ui-toggle-switch-ui
gemini mcp add --transport http made-good-designs https://madegooddesigns.com/inspiration/mcp?ref=ui-toggle-switch-ui
Add this to the app's MCP config (Windsurf: mcp_config.json).
Fonts: Fredoka + Nunito (Google Fonts)
Prompt copied: paste it into the chat that just opened.
Writing about this component?
search_ui_screens (e.g. “billing settings, editorial style”), get_ui_flow for a whole journey, and get_ui_screen_code to pull the HTML and DESIGN.md straight into your project.