47 CSS Toggles & Switches46 / 47
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.
Published Updated
The code
<label class="tgl-46">
<input class="tgl-46__input" type="checkbox" checked>
<span class="tgl-46__track" aria-hidden="true">
<span class="tgl-46__thumb"></span>
</span>
<span class="tgl-46__label">Theme: butter</span>
</label><label class="tgl-46">
<input class="tgl-46__input" type="checkbox" checked>
<span class="tgl-46__track" aria-hidden="true">
<span class="tgl-46__thumb"></span>
</span>
<span class="tgl-46__label">Theme: butter</span>
</label>.tgl-46 {
font-family: "Inter", "Segoe UI", system-ui, sans-serif;
font-size: 14px;
color: #f0eeff;
min-height: 100vh;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 24px;
padding: 48px 20px;
background: #14141e;
box-sizing: border-box;
}
.tgl-46__input {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0,0,0,0);
white-space: nowrap;
border: 0;
}
.tgl-46__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;
}
.tgl-46__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);
}
.tgl-46__input:checked ~ .tgl-46__track {
background: #f4df72;
}
.tgl-46__input:checked ~ .tgl-46__track .tgl-46__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);
}
.tgl-46__input:focus-visible ~ .tgl-46__track {
outline: 3px solid #7c6cff;
outline-offset: 4px;
}
@media (prefers-reduced-motion: reduce) {
.tgl-46__track,
.tgl-46__thumb {
transition: none;
}
}.tgl-46 {
font-family: "Inter", "Segoe UI", system-ui, sans-serif;
font-size: 14px;
color: #f0eeff;
min-height: 100vh;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 24px;
padding: 48px 20px;
background: #14141e;
box-sizing: border-box;
}
.tgl-46__input {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0,0,0,0);
white-space: nowrap;
border: 0;
}
.tgl-46__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;
}
.tgl-46__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);
}
.tgl-46__input:checked ~ .tgl-46__track {
background: #f4df72;
}
.tgl-46__input:checked ~ .tgl-46__track .tgl-46__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);
}
.tgl-46__input:focus-visible ~ .tgl-46__track {
outline: 3px solid #7c6cff;
outline-offset: 4px;
}
@media (prefers-reduced-motion: reduce) {
.tgl-46__track,
.tgl-46__thumb {
transition: none;
}
}/* No JavaScript — an 8-value border-radius pebble slides on an anticipation/overshoot bezier, mirroring its radii and tilt on landing. */
/* No JavaScript — an 8-value border-radius pebble slides on an anticipation/overshoot bezier, mirroring its radii and tilt on landing. */Here's a working CSS Toggles & Switche from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Bouncy Pebble
Source: https://codefronts.com/components/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.
## HTML
```html
<label class="tgl-46">
<input class="tgl-46__input" type="checkbox" checked>
<span class="tgl-46__track" aria-hidden="true">
<span class="tgl-46__thumb"></span>
</span>
<span class="tgl-46__label">Theme: butter</span>
</label>
```
## CSS
```css
.tgl-46 {
font-family: "Inter", "Segoe UI", system-ui, sans-serif;
font-size: 14px;
color: #f0eeff;
min-height: 100vh;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 24px;
padding: 48px 20px;
background: #14141e;
box-sizing: border-box;
}
.tgl-46__input {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0,0,0,0);
white-space: nowrap;
border: 0;
}
.tgl-46__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;
}
.tgl-46__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);
}
.tgl-46__input:checked ~ .tgl-46__track {
background: #f4df72;
}
.tgl-46__input:checked ~ .tgl-46__track .tgl-46__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);
}
.tgl-46__input:focus-visible ~ .tgl-46__track {
outline: 3px solid #7c6cff;
outline-offset: 4px;
}
@media (prefers-reduced-motion: reduce) {
.tgl-46__track,
.tgl-46__thumb {
transition: none;
}
}
```
## JavaScript
```js
/* No JavaScript — an 8-value border-radius pebble slides on an anticipation/overshoot bezier, mirroring its radii and tilt on landing. */
```Here's a working CSS Toggles & Switche from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Bouncy Pebble
Source: https://codefronts.com/components/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.
## HTML
```html
<label class="tgl-46">
<input class="tgl-46__input" type="checkbox" checked>
<span class="tgl-46__track" aria-hidden="true">
<span class="tgl-46__thumb"></span>
</span>
<span class="tgl-46__label">Theme: butter</span>
</label>
```
## CSS
```css
.tgl-46 {
font-family: "Inter", "Segoe UI", system-ui, sans-serif;
font-size: 14px;
color: #f0eeff;
min-height: 100vh;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 24px;
padding: 48px 20px;
background: #14141e;
box-sizing: border-box;
}
.tgl-46__input {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0,0,0,0);
white-space: nowrap;
border: 0;
}
.tgl-46__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;
}
.tgl-46__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);
}
.tgl-46__input:checked ~ .tgl-46__track {
background: #f4df72;
}
.tgl-46__input:checked ~ .tgl-46__track .tgl-46__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);
}
.tgl-46__input:focus-visible ~ .tgl-46__track {
outline: 3px solid #7c6cff;
outline-offset: 4px;
}
@media (prefers-reduced-motion: reduce) {
.tgl-46__track,
.tgl-46__thumb {
transition: none;
}
}
```
## JavaScript
```js
/* No JavaScript — an 8-value border-radius pebble slides on an anticipation/overshoot bezier, mirroring its radii and tilt on landing. */
```How this works
The 150×74 track is a bold outlined capsule (3px inset ring via box-shadow); the 48px thumb gets its organic silhouette from 8-value border-radius — different horizontal and vertical radii per corner — plus a −2° resting tilt for hand-drawn irregularity. Checking slides it with cubic-bezier(0.68, −0.4, 0.27, 1.4): the negative control point pulls it backward first (anticipation), the >1 endpoint overshoots before settling.
On arrival the radii MIRROR horizontally and the tilt flips to +2°, so the pebble looks resettled rather than teleported, while track and thumb swap butter/paper fills. Slide math is annotated in-source: 150px track − 54px total thumb − 10px margin = calc(100% - 64px).
Make it yours
- Reshape the pebble by editing the 8 radius values — keep left/right asymmetry for the organic read.
- Exaggerate the physics: −0.6 and 1.6 in the bezier for full cartoon.
- Swap the butter palette via the two background hexes.
- Shrink to form-scale (~90×44) — the radii are percentages and scale cleanly.
Gotchas — read before shipping
- The bezier's overshoot needs the 10px end margins as headroom or the thumb visually kisses the ring.
- transition:all here covers left + radius + rotation together — list them explicitly if you add more properties later.
- Mirror the radii on state change; reusing one blob shape makes the flip read as a slide-only.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 26+ | 7+ | 16+ | 26+ |
8-value border-radius + custom beziers; universal in modern browsers.