{ CF }

20 Pure CSS Toggles & Switches

Bouncy Pebble

A chunky outlined toggle with an organic "pebble" thumb shape built from 8-value border-radius syntax. Bouncy springy transition with anticipation + overshoot. Track and thumb swap colors on each state. The reference for hand-crafted, designer-drawn toggles — distinctive from every other demo in the collection.

Pure CSS MIT licensed

Bouncy Pebble the 19th of 20 designs in the 20 Pure CSS Toggles & Switches collection. The design is implemented in pure CSS — no JavaScript required. Copy the HTML and CSS panels below into your project. Because the demo is pure CSS, it works in any framework or templating engine you happen to use. The design honours prefers-reduced-motion and uses real semantic markup, so it ships accessibility-ready out of the box.

Live preview

Open in playground

The code

<label class="tg-bp">
  <input class="tg-bp-input" type="checkbox" checked>
  <span class="tg-bp-track" aria-hidden="true">
    <span class="tg-bp-thumb"></span>
  </span>
  <span class="tg-bp-label">Theme: butter</span>
</label>
.tg-bp {
  display: inline-flex;
  align-items: center;
  gap: 16px;
  cursor: pointer;
  font-family: "Inter", "Segoe UI", system-ui, sans-serif;
  font-size: 14px;
  color: #f0eeff;
  user-select: none;
}
.tg-bp-input {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0);
  white-space: nowrap; border: 0;
}
.tg-bp-track {
  position: relative;
  width: 150px;
  height: 74px;
  background: #f3f3f3;
  border-radius: 999px;
  box-shadow:
    inset 0 0 0 3px #2a2a2a,
    0 10px 20px rgba(0,0,0,0.35);
  transition: background 0.35s ease;
}
.tg-bp-thumb {
  position: absolute;
  top: 50%;
  left: 10px;
  width: 48px;
  height: 48px;
  background: #f4df72;
  border: 3px solid #2b2b2b;
  box-shadow: 0 5px 10px rgba(0,0,0,0.25);
  /* 8-value border-radius (horizontal × vertical radii per corner)
     creates the organic asymmetric pebble shape: fuller left, tighter
     right, subtle diagonal tension. The -2deg tilt adds hand-crafted
     irregularity. The cubic-bezier easing has anticipation (negative
     control point) and overshoot (>1 control point) — the thumb
     pulls back slightly before sliding, then overshoots the
     destination before settling. */
  border-radius: 52% 38% 42% 58% / 52% 42% 58% 48%;
  transform: translateY(-50%) rotate(-2deg);
  transition: all 0.35s cubic-bezier(0.68, -0.4, 0.27, 1.4);
}
.tg-bp-input:checked ~ .tg-bp-track {
  background: #f4df72;
}
.tg-bp-input:checked ~ .tg-bp-track .tg-bp-thumb {
  /* Slides to right edge. Track is 150px wide, thumb is 48px + 3px×2
     border = 54px total. left: calc(100% - 64px) gives 10px from the
     right edge, mirroring the 10px from the left in the off state. */
  left: calc(100% - 64px);
  background: #f5f5f5;
  /* Mirrored border-radius — horizontal asymmetry flips. The "fuller"
     side is now the right (the direction the thumb just came from). */
  border-radius: 38% 52% 58% 42% / 42% 52% 48% 58%;
  transform: translateY(-50%) rotate(2deg);
}
.tg-bp-input:focus-visible ~ .tg-bp-track {
  outline: 3px solid #7c6cff;
  outline-offset: 4px;
}
@media (prefers-reduced-motion: reduce) {
  .tg-bp-track,
  .tg-bp-thumb { transition: none; }
}

Search CodeFronts

Loading…