Open demo

CSS code

HTML
<div><span class="lbl">Stretching knob (press and hold)</span><div class="row">
<label class="sq"><input type="checkbox" role="switch" aria-label="Autoplay" checked><i aria-hidden="true"></i></label>
<label class="sq"><input type="checkbox" role="switch" aria-label="Reminders"><i aria-hidden="true"></i></label></div></div>
<div><span class="lbl">Mood switch</span><div class="row">
<label class="fc"><input type="checkbox" role="switch" aria-label="Good mood" checked><i aria-hidden="true"></i></label>
<label class="fc"><input type="checkbox" role="switch" aria-label="Party mode"><i aria-hidden="true"></i></label></div></div>
<div><span class="lbl">Padlock</span><div class="row">
<label class="lk"><input type="checkbox" aria-label="Lock profile" checked><i aria-hidden="true"></i></label>
<label class="lk"><input type="checkbox" aria-label="Lock folder"><i aria-hidden="true"></i></label></div></div>
CSS
/* Playful switches: a visually hidden checkbox followed by an <i> that draws the control. */
.sq input,.fc input,.lk input{position:absolute;opacity:0;width:0;height:0;margin:0}
/* stretching knob: the knob widens while pressed */
.sq{position:relative;display:inline-block;width:78px;height:36px;cursor:pointer}
.sq i{position:absolute;inset:0;border-radius:999px;background:#7c8aa0;transition:background .3s}
.sq i::before{content:"";position:absolute;top:4px;left:4px;width:28px;height:28px;border-radius:999px;
  background:#fff;box-shadow:0 1px 3px rgba(15,23,42,.3);
  transition:transform .34s cubic-bezier(.5,-0.4,.5,1.4),width .34s}
.sq:active i::before{width:40px}
.sq input:checked+i{background:#d97706}
.sq input:checked+i::before{transform:translateX(42px)}
.sq:active input:checked+i::before{transform:translateX(30px)}
/* mood switch */
.fc{position:relative;display:inline-block;width:70px;height:34px;cursor:pointer}
.fc i{position:absolute;inset:0;border-radius:999px;background:#7c8aa0;transition:background .3s}
.fc i::before{content:"☹";position:absolute;top:3px;left:3px;width:28px;height:28px;border-radius:50%;
  background:#fff;display:grid;place-items:center;font-size:1rem;font-style:normal;color:#64748b;
  box-shadow:0 1px 3px rgba(15,23,42,.25);transition:transform .34s}
.fc input:checked+i{background:#ef4444}
.fc input:checked+i::before{content:"☺";transform:translateX(36px) rotate(360deg);color:#dc2626}
/* padlock */
.lk{position:relative;width:56px;height:56px;display:inline-block;cursor:pointer}
.lk i{position:absolute;inset:0;border-radius:14px;background:#e2e8f0;display:grid;place-items:center;
  font-size:1.4rem;font-style:normal;color:#64748b;transition:background .25s,color .25s}
.lk i::after{content:"🔓"}
.lk input:checked+i{background:#0f172a;color:#fff}
.lk input:checked+i::after{content:"🔒"}
.sq input:focus-visible+i,.fc input:focus-visible+i,.lk input:focus-visible+i{outline:2px solid #2563eb;outline-offset:3px}
@media (prefers-reduced-motion:reduce){.sq i,.sq i::before,.fc i,.fc i::before,.lk i{transition:none}}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div><span class="lbl">Stretching knob (press and hold)</span><div class="demo-row">
<label class="sq"><input class="visually-hidden" type="checkbox" role="switch" aria-label="Autoplay" checked><i class="rounded-pill" aria-hidden="true"></i></label>
<label class="sq"><input class="visually-hidden" type="checkbox" role="switch" aria-label="Reminders"><i class="rounded-pill" aria-hidden="true"></i></label></div></div>
<div><span class="lbl">Mood switch</span><div class="demo-row">
<label class="fc"><input class="visually-hidden" type="checkbox" role="switch" aria-label="Good mood" checked><i class="rounded-pill" aria-hidden="true"></i></label>
<label class="fc"><input class="visually-hidden" type="checkbox" role="switch" aria-label="Party mode"><i class="rounded-pill" aria-hidden="true"></i></label></div></div>
<div><span class="lbl">Padlock</span><div class="demo-row">
<div class="lk"><input type="checkbox" class="btn-check" id="lk-profile" aria-label="Lock profile" checked><label aria-hidden="true" class="btn d-grid align-content-center" for="lk-profile"></label></div>
<div class="lk"><input type="checkbox" class="btn-check" id="lk-folder" aria-label="Lock folder"><label aria-hidden="true" class="btn d-grid align-content-center" for="lk-folder"></label></div></div></div>
CSS
/* Playful switches. The knob art (stretch, flipping face) needs its own element, so these use a Bootstrap
   .visually-hidden checkbox and an <i>; the padlock is a .btn-check + .btn tile. */
.sq{position:relative;display:inline-block;width:78px;height:36px;cursor:pointer}
.sq i{position:absolute;inset:0;background:#7c8aa0;transition:background .3s}
.sq i::before{content:"";position:absolute;top:4px;left:4px;width:28px;height:28px;border-radius:999px;
  background:#fff;box-shadow:0 1px 3px rgba(15,23,42,.3);
  transition:transform .34s cubic-bezier(.5,-0.4,.5,1.4),width .34s}
.sq:active i::before{width:40px}
.sq input:checked+i{background:#d97706}
.sq input:checked+i::before{transform:translateX(42px)}
.sq:active input:checked+i::before{transform:translateX(30px)}
/* mood switch */
.fc{position:relative;display:inline-block;width:70px;height:34px;cursor:pointer}
.fc i{position:absolute;inset:0;background:#7c8aa0;transition:background .3s}
.fc i::before{content:"☹";position:absolute;top:3px;left:3px;width:28px;height:28px;border-radius:50%;
  background:#fff;display:grid;place-items:center;font-size:1rem;font-style:normal;color:#64748b;
  box-shadow:0 1px 3px rgba(15,23,42,.25);transition:transform .34s}
.fc input:checked+i{background:#ef4444}
.fc input:checked+i::before{content:"☺";transform:translateX(36px) rotate(360deg);color:#dc2626}
.sq input:focus-visible+i,.fc input:focus-visible+i{outline:2px solid #2563eb;outline-offset:3px}
/* padlock */
.lk .btn{--bs-btn-padding-x:0;--bs-btn-padding-y:0;--bs-btn-border-width:0;--bs-btn-border-radius:14px;
  --bs-btn-font-size:1.4rem;--bs-btn-line-height:1;--bs-btn-bg:#e2e8f0;--bs-btn-color:#64748b;--bs-btn-hover-bg:#e2e8f0;
  --bs-btn-hover-color:#64748b;--bs-btn-active-bg:#0f172a;--bs-btn-active-color:#fff;
  --bs-btn-focus-box-shadow:0 0 0 3px #f8fafc,0 0 0 5px #2563eb;
  width:56px;height:56px;transition:background-color .25s,color .25s}
.lk .btn::after{content:"🔓"}
.lk .btn-check:checked+.btn::after{content:"🔒"}
@media (prefers-reduced-motion:reduce){.sq i,.sq i::before,.fc i,.fc i::before,.lk .btn{transition:none}}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#64748b] uppercase">Stretching knob (press and hold)</span><div class="flex flex-wrap items-center gap-[18px]">
<label class="group relative inline-block h-9 w-[78px] cursor-pointer"><input type="checkbox" role="switch" aria-label="Autoplay" class="peer sr-only" checked><span class="absolute inset-0 rounded-full bg-play-off transition-[background-color,border-color,color] duration-300 peer-checked:bg-play-amber peer-focus-visible:outline-2 peer-focus-visible:outline-offset-3 peer-focus-visible:outline-play-focus motion-reduce:transition-none"></span><span class="pointer-events-none absolute top-1 left-1 h-7 w-7 rounded-full bg-white shadow-[0_1px_3px_rgba(15,23,42,.3)] transition-[translate,width] duration-[340ms] ease-springy group-active:w-10 peer-checked:translate-x-[42px] peer-checked:group-active:translate-x-[30px] motion-reduce:transition-none"></span></label>
<label class="group relative inline-block h-9 w-[78px] cursor-pointer"><input type="checkbox" role="switch" aria-label="Reminders" class="peer sr-only"><span class="absolute inset-0 rounded-full bg-play-off transition-[background-color,border-color,color] duration-300 peer-checked:bg-play-amber peer-focus-visible:outline-2 peer-focus-visible:outline-offset-3 peer-focus-visible:outline-play-focus motion-reduce:transition-none"></span><span class="pointer-events-none absolute top-1 left-1 h-7 w-7 rounded-full bg-white shadow-[0_1px_3px_rgba(15,23,42,.3)] transition-[translate,width] duration-[340ms] ease-springy group-active:w-10 peer-checked:translate-x-[42px] peer-checked:group-active:translate-x-[30px] motion-reduce:transition-none"></span></label></div></div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#64748b] uppercase">Mood switch</span><div class="flex flex-wrap items-center gap-[18px]">
<label class="relative inline-block h-[34px] w-[70px] cursor-pointer"><input type="checkbox" role="switch" aria-label="Good mood" class="peer sr-only" checked><span class="absolute inset-0 rounded-full bg-play-off transition-[background-color,border-color,color] duration-300 peer-checked:bg-play-red peer-focus-visible:outline-2 peer-focus-visible:outline-offset-3 peer-focus-visible:outline-play-focus motion-reduce:transition-none"></span><span aria-hidden="true" class="pointer-events-none absolute top-[3px] left-[3px] grid size-7 place-items-center rounded-full bg-white text-base leading-[normal] text-[#64748b] shadow-[0_1px_3px_rgba(15,23,42,.25)] transition-[translate,rotate] duration-[340ms] before:content-['☹'] peer-checked:translate-x-9 peer-checked:rotate-[360deg] peer-checked:text-play-face peer-checked:before:content-['☺'] motion-reduce:transition-none"></span></label>
<label class="relative inline-block h-[34px] w-[70px] cursor-pointer"><input type="checkbox" role="switch" aria-label="Party mode" class="peer sr-only"><span class="absolute inset-0 rounded-full bg-play-off transition-[background-color,border-color,color] duration-300 peer-checked:bg-play-red peer-focus-visible:outline-2 peer-focus-visible:outline-offset-3 peer-focus-visible:outline-play-focus motion-reduce:transition-none"></span><span aria-hidden="true" class="pointer-events-none absolute top-[3px] left-[3px] grid size-7 place-items-center rounded-full bg-white text-base leading-[normal] text-[#64748b] shadow-[0_1px_3px_rgba(15,23,42,.25)] transition-[translate,rotate] duration-[340ms] before:content-['☹'] peer-checked:translate-x-9 peer-checked:rotate-[360deg] peer-checked:text-play-face peer-checked:before:content-['☺'] motion-reduce:transition-none"></span></label></div></div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#64748b] uppercase">Padlock</span><div class="flex flex-wrap items-center gap-[18px]">
<label class="relative inline-block size-14 cursor-pointer"><input type="checkbox" aria-label="Lock profile" class="peer sr-only" checked><span aria-hidden="true" class="absolute inset-0 grid place-items-center rounded-[14px] bg-play-tile text-[1.4rem] leading-[normal] text-[#64748b] transition-[background-color,border-color,color] duration-[250ms] after:content-['🔓'] peer-checked:bg-play-ink peer-checked:text-white peer-checked:after:content-['🔒'] peer-focus-visible:outline-2 peer-focus-visible:outline-offset-3 peer-focus-visible:outline-play-focus motion-reduce:transition-none"></span></label>
<label class="relative inline-block size-14 cursor-pointer"><input type="checkbox" aria-label="Lock folder" class="peer sr-only"><span aria-hidden="true" class="absolute inset-0 grid place-items-center rounded-[14px] bg-play-tile text-[1.4rem] leading-[normal] text-[#64748b] transition-[background-color,border-color,color] duration-[250ms] after:content-['🔓'] peer-checked:bg-play-ink peer-checked:text-white peer-checked:after:content-['🔒'] peer-focus-visible:outline-2 peer-focus-visible:outline-offset-3 peer-focus-visible:outline-play-focus motion-reduce:transition-none"></span></label></div></div>
CSS
@theme {
  --color-play-off: #7c8aa0;
  --color-play-amber: #d97706;
  --color-play-red: #ef4444;
  --color-play-face: #dc2626;
  --color-play-tile: #e2e8f0;
  --color-play-ink: #0f172a;
  --color-play-focus: #2563eb;
  --ease-springy: cubic-bezier(.5, -0.4, .5, 1.4);
}

About this set

Switches with some personality, for games, onboarding flows, kids products and marketing pages. The stretching knob widens while you press and hold, then springs across with an overshooting easing curve, and the amber track shows the on state. The mood switch has a face on its knob that spins a full turn and changes from a frown to a smile as the track turns red. The padlock is a 56 pixel tile showing an open lock that becomes a closed lock on a dark tile when switched on. Use them sparingly, where a moment of fun suits the product, and keep the classic set for ordinary settings. Each one is a visually hidden checkbox followed by an element; the stretch uses the :active state of the label, and the faces and locks are CSS generated content swapped by :checked. The Bootstrap version uses a .btn-check and .btn tile for the padlock, and the Tailwind version uses group-active and peer-checked variants.

What’s included

  • Knob that stretches on press and springs across
  • Mood face that spins and changes from frown to smile
  • Padlock tile that closes when switched on
  • Tracks darkened so the off and on states reach 3:1
  • All motion removed under prefers-reduced-motion

Accessibility

Every control has an aria-label, and the stretch and mood controls use role="switch". The glyphs are decorative and hidden from assistive technology. Tab and Space work as for any checkbox, with a 2px blue focus outline around the visible control. The stretch, spin and colour transitions are all removed when prefers-reduced-motion is set.

Customise it

Change the track colours #7c8aa0, #d97706 and #ef4444 in .sq and .fc, and the tile colours in .lk (plain CSS and Bootstrap), or the --color-play-* theme colours and --ease-springy (Tailwind). The spring comes from the cubic-bezier timing function on the knob.