35 / 47
All 47 in 47 CSS Toggles & Switches
- 01Minimal iOS-Style Toggle Switch
- 02Dark Mode Sun / Moon Theme Switcher
- 033D Neumorphic Pill Toggle
- 04Gooey Liquid Morphing Switch
- 05Bouncy Elastic Physics Toggle
- 06Inner Text ON / OFF Switch
- 07SaaS Pricing Segmented Pill Toggle
- 08Haptic Mechanical Keycap Toggles
- 09Waveform Mute Toggle
- 10Timed Auto-Off Toggle
- 11Biometric Hold-to-Confirm Scan Toggle
- 12Split-Flap Departure Board Toggle
- 13Permission Stack with Live Status
- 14Material Design 3 Switch
- 15Skeuomorphic Wall Light Switch
- 16Neon Cyberpunk Toggle
- 17Brutalist Snap Toggle
- 18Day / Night Landscape Scene Switch
- 193D Flip Card Toggle
- 20Sliding Pill with Traveling Label
- 21Glassmorphism Frosted Toggle
- 22Skeuomorphic Rotary Knob Toggle
- 23iOS Light Mode Settings List
- 24Outline Minimal currentColor Toggle
- 25Bouncy Pebble Organic Toggle
- 26Industrial Rocker I/O Switch
- 27Accessible ARIA role=switch Toggle
- 28Haptic Keys
- 29Waveform Toggle
- 30Timed Toggle
- 31Biometric Scan
- 32Split-Flap Board
- 33Permission Stack
- 34iOS
- 35Material Design
- 36Skeuomorphic Light Switch
- 37Neon Cyber
- 38Brutalist
- 39Day / Night
- 40Flip Card
- 41Sliding Pill with Label
- 42Glassmorphism
- 43Skeuomorphic Knob
- 44iOS Light Mode
- 45Outline
- 46Bouncy Pebble
- 47Industrial Rocker
Material Design
Google Material Design 3 switch — track with rounded thumb that lifts and changes colour on toggle. Active state shows a filled icon-style indicator.
Build interactive UI like this from scratch: 20 HTML, CSS & JavaScript projects
ZTM · JavaScript Web Projects · 30‑day money-back guarantee · Affiliate link
The code
<label class="tgl-35">
<input class="tgl-35__input" type="checkbox" checked>
<span class="tgl-35__track" aria-hidden="true">
<span class="tgl-35__thumb"></span>
</span>
<span class="tgl-35__label">Notifications</span>
</label><label class="tgl-35">
<input class="tgl-35__input" type="checkbox" checked>
<span class="tgl-35__track" aria-hidden="true">
<span class="tgl-35__thumb"></span>
</span>
<span class="tgl-35__label">Notifications</span>
</label>.tgl-35 {
font-family: "Roboto", "Google Sans", sans-serif;
font-size: 14px;
color: #e6e0e9;
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-35__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-35__track {
position: relative;
width: 52px;
height: 32px;
background: #49454f;
border: 2px solid #79747e;
border-radius: 999px;
transition: background 0.2s ease, border-color 0.2s ease;
box-sizing: border-box;
}
.tgl-35__thumb {
position: absolute;
top: 50%;
left: 6px;
width: 16px;
height: 16px;
border-radius: 50%;
background: #cac4d0;
transform: translateY(-50%);
transition: left 0.2s cubic-bezier(0.4, 0, 0.2, 1), width 0.15s ease, height 0.15s ease, background 0.2s ease;
}
.tgl-35__input:checked ~ .tgl-35__track {
background: #d0bcff;
border-color: #d0bcff;
}
.tgl-35__input:checked ~ .tgl-35__track .tgl-35__thumb {
/* Inner width is 48px (52 - 2px border × 2). Thumb grows to 24px
on-state, so max left value that keeps the thumb fully inside the
border is 48 - 24 = 24px, minus 2px breathing room = 22px. */
left: 22px;
width: 24px;
height: 24px;
background: #381e72;
}
.tgl-35__input:focus-visible ~ .tgl-35__track {
outline: 2px solid #d0bcff;
outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
.tgl-35__track,
.tgl-35__thumb {
transition: none;
}
}.tgl-35 {
font-family: "Roboto", "Google Sans", sans-serif;
font-size: 14px;
color: #e6e0e9;
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-35__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-35__track {
position: relative;
width: 52px;
height: 32px;
background: #49454f;
border: 2px solid #79747e;
border-radius: 999px;
transition: background 0.2s ease, border-color 0.2s ease;
box-sizing: border-box;
}
.tgl-35__thumb {
position: absolute;
top: 50%;
left: 6px;
width: 16px;
height: 16px;
border-radius: 50%;
background: #cac4d0;
transform: translateY(-50%);
transition: left 0.2s cubic-bezier(0.4, 0, 0.2, 1), width 0.15s ease, height 0.15s ease, background 0.2s ease;
}
.tgl-35__input:checked ~ .tgl-35__track {
background: #d0bcff;
border-color: #d0bcff;
}
.tgl-35__input:checked ~ .tgl-35__track .tgl-35__thumb {
/* Inner width is 48px (52 - 2px border × 2). Thumb grows to 24px
on-state, so max left value that keeps the thumb fully inside the
border is 48 - 24 = 24px, minus 2px breathing room = 22px. */
left: 22px;
width: 24px;
height: 24px;
background: #381e72;
}
.tgl-35__input:focus-visible ~ .tgl-35__track {
outline: 2px solid #d0bcff;
outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
.tgl-35__track,
.tgl-35__thumb {
transition: none;
}
}/* No JavaScript — the M3 thumb grows 16→24px and shifts left:6→22px on separate transitions while track and border merge to the tonal fill. */
/* No JavaScript — the M3 thumb grows 16→24px and shifts left:6→22px on separate transitions while track and border merge to the tonal fill. */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: Material Design
Source: https://codefronts.com/components/css-toggles-switches/material-design/
Google Material Design 3 switch — track with rounded thumb that lifts and changes colour on toggle. Active state shows a filled icon-style indicator.
## HTML
```html
<label class="tgl-35">
<input class="tgl-35__input" type="checkbox" checked>
<span class="tgl-35__track" aria-hidden="true">
<span class="tgl-35__thumb"></span>
</span>
<span class="tgl-35__label">Notifications</span>
</label>
```
## CSS
```css
.tgl-35 {
font-family: "Roboto", "Google Sans", sans-serif;
font-size: 14px;
color: #e6e0e9;
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-35__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-35__track {
position: relative;
width: 52px;
height: 32px;
background: #49454f;
border: 2px solid #79747e;
border-radius: 999px;
transition: background 0.2s ease, border-color 0.2s ease;
box-sizing: border-box;
}
.tgl-35__thumb {
position: absolute;
top: 50%;
left: 6px;
width: 16px;
height: 16px;
border-radius: 50%;
background: #cac4d0;
transform: translateY(-50%);
transition: left 0.2s cubic-bezier(0.4, 0, 0.2, 1), width 0.15s ease, height 0.15s ease, background 0.2s ease;
}
.tgl-35__input:checked ~ .tgl-35__track {
background: #d0bcff;
border-color: #d0bcff;
}
.tgl-35__input:checked ~ .tgl-35__track .tgl-35__thumb {
/* Inner width is 48px (52 - 2px border × 2). Thumb grows to 24px
on-state, so max left value that keeps the thumb fully inside the
border is 48 - 24 = 24px, minus 2px breathing room = 22px. */
left: 22px;
width: 24px;
height: 24px;
background: #381e72;
}
.tgl-35__input:focus-visible ~ .tgl-35__track {
outline: 2px solid #d0bcff;
outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
.tgl-35__track,
.tgl-35__thumb {
transition: none;
}
}
```
## JavaScript
```js
/* No JavaScript — the M3 thumb grows 16→24px and shifts left:6→22px on separate transitions while track and border merge to the tonal fill. */
```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: Material Design
Source: https://codefronts.com/components/css-toggles-switches/material-design/
Google Material Design 3 switch — track with rounded thumb that lifts and changes colour on toggle. Active state shows a filled icon-style indicator.
## HTML
```html
<label class="tgl-35">
<input class="tgl-35__input" type="checkbox" checked>
<span class="tgl-35__track" aria-hidden="true">
<span class="tgl-35__thumb"></span>
</span>
<span class="tgl-35__label">Notifications</span>
</label>
```
## CSS
```css
.tgl-35 {
font-family: "Roboto", "Google Sans", sans-serif;
font-size: 14px;
color: #e6e0e9;
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-35__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-35__track {
position: relative;
width: 52px;
height: 32px;
background: #49454f;
border: 2px solid #79747e;
border-radius: 999px;
transition: background 0.2s ease, border-color 0.2s ease;
box-sizing: border-box;
}
.tgl-35__thumb {
position: absolute;
top: 50%;
left: 6px;
width: 16px;
height: 16px;
border-radius: 50%;
background: #cac4d0;
transform: translateY(-50%);
transition: left 0.2s cubic-bezier(0.4, 0, 0.2, 1), width 0.15s ease, height 0.15s ease, background 0.2s ease;
}
.tgl-35__input:checked ~ .tgl-35__track {
background: #d0bcff;
border-color: #d0bcff;
}
.tgl-35__input:checked ~ .tgl-35__track .tgl-35__thumb {
/* Inner width is 48px (52 - 2px border × 2). Thumb grows to 24px
on-state, so max left value that keeps the thumb fully inside the
border is 48 - 24 = 24px, minus 2px breathing room = 22px. */
left: 22px;
width: 24px;
height: 24px;
background: #381e72;
}
.tgl-35__input:focus-visible ~ .tgl-35__track {
outline: 2px solid #d0bcff;
outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
.tgl-35__track,
.tgl-35__thumb {
transition: none;
}
}
```
## JavaScript
```js
/* No JavaScript — the M3 thumb grows 16→24px and shifts left:6→22px on separate transitions while track and border merge to the tonal fill. */
```How this works
M3's defining trait is the state-carrying thumb: unchecked it's a small 16px dot in outline color resting 6px from the left of the 52×32 outlined track; checked it grows to 24px, deepens to the on-primary tone (#381e72 on a #d0bcff track) and moves to left:22px — the exact spec sum (48px inner − 24px thumb − 2px breathing room).
Growth and travel are separate transitions (width/height at 0.15s, left at 0.2s on the standard curve) so the thumb visibly swells as it arrives, matching the Material motion spec's emphasis. Track fill and border collapse to the same tonal color when checked, per M3.
Make it yours
- Retheme with your own M3 tonal pair (track/thumb) — the geometry is spec-fixed.
- Add the pressed 28px state with :active width/height rules.
- Drop in a checkmark icon via a masked ::after (see demo 14).
- Light-scheme variant: swap the four color values only.
Gotchas — read before shipping
- The left-position math must track any resize: inner width − thumb − 2px; hardcoded 22px only works at spec size.
- This variant animates width/height/left for spec fidelity on a 24px element — negligible, but demo 14 shows the transform-only approach if you're INP-strict.
- Keep the 2px outline in the off state — it's M3's required affordance that the control is interactive.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 26+ | 9+ | 16+ | 26+ |
Plain transitions + sibling :checked; universal in modern browsers.