17 / 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
Brutalist Snap Toggle
Anti-animation as a statement: a raw, square switch with 3px black borders and a hard offset shadow. The block thumb doesn't glide — it SNAPS to the other side in a single 90ms step, the shadow jumps, and the state stamps itself in uppercase mono. Zero easing, zero apology.
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
<section class="tgl-17" aria-label="Brutalist snap toggle">
<label class="tgl-17__card" for="tgl-17-sw">
<span class="tgl-17__lbl">NEWSLETTER</span>
<span class="tgl-17__unit">
<input type="checkbox" id="tgl-17-sw" class="tgl-17__sw" role="switch">
<span class="tgl-17__state" aria-hidden="true"><i>OFF</i><i>ON</i></span>
</span>
</label>
<p class="tgl-17__cap">steps(1) — it snaps, it does not glide</p>
</section><section class="tgl-17" aria-label="Brutalist snap toggle">
<label class="tgl-17__card" for="tgl-17-sw">
<span class="tgl-17__lbl">NEWSLETTER</span>
<span class="tgl-17__unit">
<input type="checkbox" id="tgl-17-sw" class="tgl-17__sw" role="switch">
<span class="tgl-17__state" aria-hidden="true"><i>OFF</i><i>ON</i></span>
</span>
</label>
<p class="tgl-17__cap">steps(1) — it snaps, it does not glide</p>
</section>.tgl-17,
.tgl-17 *,
.tgl-17 *::before,
.tgl-17 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.tgl-17 {
font-family: ui-monospace,'SF Mono',Menlo,monospace;
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 30px;
padding: 60px 20px;
background: #f0ede6;
}
.tgl-17__card {
display: flex;
align-items: center;
gap: 30px;
background: #fff;
border: 3px solid #000;
box-shadow: 6px 6px 0 #000;
padding: 22px 28px;
cursor: pointer;
transition: transform .09s steps(1,end),box-shadow .09s steps(1,end);
}
.tgl-17__card:active {
transform: translate(3px,3px);
box-shadow: 3px 3px 0 #000;
}
.tgl-17__lbl {
font-size: 15px;
font-weight: 700;
letter-spacing: .14em;
color: #000;
}
.tgl-17__unit {
position: relative;
display: block;
}
.tgl-17__sw {
appearance: none;
-webkit-appearance: none;
display: block;
width: 104px;
height: 48px;
border: 3px solid #000;
background: #fff;
cursor: pointer;
position: relative;
transition: background-color .09s steps(1,end);
}
.tgl-17__sw:checked {
background: #ffe600;
}
.tgl-17__sw::before {
content: "";
position: absolute;
top: 3px;
left: 3px;
width: 36px;
height: 36px;
background: #000;
transition: transform .09s steps(1,end);
}
.tgl-17__sw:checked::before {
transform: translateX(56px);
}
.tgl-17__state {
position: absolute;
inset: 3px;
display: block;
pointer-events: none;
font-size: 13px;
font-weight: 800;
letter-spacing: .1em;
color: #000;
}
.tgl-17__state i {
position: absolute;
top: 50%;
transform: translateY(-50%);
font-style: normal;
}
.tgl-17__state i:nth-child(1) {
right: 10px;
}
.tgl-17__state i:nth-child(2) {
left: 10px;
display: none;
}
.tgl-17__sw:checked ~ .tgl-17__state i:nth-child(1) {
display: none;
}
.tgl-17__sw:checked ~ .tgl-17__state i:nth-child(2) {
display: block;
}
.tgl-17__sw:focus-visible {
outline: 4px solid #000;
outline-offset: 4px;
}
.tgl-17__cap {
font-size: 12px;
letter-spacing: .1em;
color: #8a8578;
}
@media (prefers-reduced-motion: reduce) {
.tgl-17__card,
.tgl-17__sw,
.tgl-17__sw::before {
transition: none;
}
}.tgl-17,
.tgl-17 *,
.tgl-17 *::before,
.tgl-17 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.tgl-17 {
font-family: ui-monospace,'SF Mono',Menlo,monospace;
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 30px;
padding: 60px 20px;
background: #f0ede6;
}
.tgl-17__card {
display: flex;
align-items: center;
gap: 30px;
background: #fff;
border: 3px solid #000;
box-shadow: 6px 6px 0 #000;
padding: 22px 28px;
cursor: pointer;
transition: transform .09s steps(1,end),box-shadow .09s steps(1,end);
}
.tgl-17__card:active {
transform: translate(3px,3px);
box-shadow: 3px 3px 0 #000;
}
.tgl-17__lbl {
font-size: 15px;
font-weight: 700;
letter-spacing: .14em;
color: #000;
}
.tgl-17__unit {
position: relative;
display: block;
}
.tgl-17__sw {
appearance: none;
-webkit-appearance: none;
display: block;
width: 104px;
height: 48px;
border: 3px solid #000;
background: #fff;
cursor: pointer;
position: relative;
transition: background-color .09s steps(1,end);
}
.tgl-17__sw:checked {
background: #ffe600;
}
.tgl-17__sw::before {
content: "";
position: absolute;
top: 3px;
left: 3px;
width: 36px;
height: 36px;
background: #000;
transition: transform .09s steps(1,end);
}
.tgl-17__sw:checked::before {
transform: translateX(56px);
}
.tgl-17__state {
position: absolute;
inset: 3px;
display: block;
pointer-events: none;
font-size: 13px;
font-weight: 800;
letter-spacing: .1em;
color: #000;
}
.tgl-17__state i {
position: absolute;
top: 50%;
transform: translateY(-50%);
font-style: normal;
}
.tgl-17__state i:nth-child(1) {
right: 10px;
}
.tgl-17__state i:nth-child(2) {
left: 10px;
display: none;
}
.tgl-17__sw:checked ~ .tgl-17__state i:nth-child(1) {
display: none;
}
.tgl-17__sw:checked ~ .tgl-17__state i:nth-child(2) {
display: block;
}
.tgl-17__sw:focus-visible {
outline: 4px solid #000;
outline-offset: 4px;
}
.tgl-17__cap {
font-size: 12px;
letter-spacing: .1em;
color: #8a8578;
}
@media (prefers-reduced-motion: reduce) {
.tgl-17__card,
.tgl-17__sw,
.tgl-17__sw::before {
transition: none;
}
}/* No JavaScript — steps(1,end) timing snaps the block thumb in a single frame, and :active pushes the card into its hard offset shadow. */
/* No JavaScript — steps(1,end) timing snaps the block thumb in a single frame, and :active pushes the card into its hard offset shadow. */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: Brutalist Snap Toggle
Source: https://codefronts.com/components/css-toggles-switches/brutalist-snap-toggle/
Anti-animation as a statement: a raw, square switch with 3px black borders and a hard offset shadow. The block thumb doesn't glide — it SNAPS to the other side in a single 90ms step, the shadow jumps, and the state stamps itself in uppercase mono. Zero easing, zero apology.
## HTML
```html
<section class="tgl-17" aria-label="Brutalist snap toggle">
<label class="tgl-17__card" for="tgl-17-sw">
<span class="tgl-17__lbl">NEWSLETTER</span>
<span class="tgl-17__unit">
<input type="checkbox" id="tgl-17-sw" class="tgl-17__sw" role="switch">
<span class="tgl-17__state" aria-hidden="true"><i>OFF</i><i>ON</i></span>
</span>
</label>
<p class="tgl-17__cap">steps(1) — it snaps, it does not glide</p>
</section>
```
## CSS
```css
.tgl-17,
.tgl-17 *,
.tgl-17 *::before,
.tgl-17 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.tgl-17 {
font-family: ui-monospace,'SF Mono',Menlo,monospace;
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 30px;
padding: 60px 20px;
background: #f0ede6;
}
.tgl-17__card {
display: flex;
align-items: center;
gap: 30px;
background: #fff;
border: 3px solid #000;
box-shadow: 6px 6px 0 #000;
padding: 22px 28px;
cursor: pointer;
transition: transform .09s steps(1,end),box-shadow .09s steps(1,end);
}
.tgl-17__card:active {
transform: translate(3px,3px);
box-shadow: 3px 3px 0 #000;
}
.tgl-17__lbl {
font-size: 15px;
font-weight: 700;
letter-spacing: .14em;
color: #000;
}
.tgl-17__unit {
position: relative;
display: block;
}
.tgl-17__sw {
appearance: none;
-webkit-appearance: none;
display: block;
width: 104px;
height: 48px;
border: 3px solid #000;
background: #fff;
cursor: pointer;
position: relative;
transition: background-color .09s steps(1,end);
}
.tgl-17__sw:checked {
background: #ffe600;
}
.tgl-17__sw::before {
content: "";
position: absolute;
top: 3px;
left: 3px;
width: 36px;
height: 36px;
background: #000;
transition: transform .09s steps(1,end);
}
.tgl-17__sw:checked::before {
transform: translateX(56px);
}
.tgl-17__state {
position: absolute;
inset: 3px;
display: block;
pointer-events: none;
font-size: 13px;
font-weight: 800;
letter-spacing: .1em;
color: #000;
}
.tgl-17__state i {
position: absolute;
top: 50%;
transform: translateY(-50%);
font-style: normal;
}
.tgl-17__state i:nth-child(1) {
right: 10px;
}
.tgl-17__state i:nth-child(2) {
left: 10px;
display: none;
}
.tgl-17__sw:checked ~ .tgl-17__state i:nth-child(1) {
display: none;
}
.tgl-17__sw:checked ~ .tgl-17__state i:nth-child(2) {
display: block;
}
.tgl-17__sw:focus-visible {
outline: 4px solid #000;
outline-offset: 4px;
}
.tgl-17__cap {
font-size: 12px;
letter-spacing: .1em;
color: #8a8578;
}
@media (prefers-reduced-motion: reduce) {
.tgl-17__card,
.tgl-17__sw,
.tgl-17__sw::before {
transition: none;
}
}
```
## JavaScript
```js
/* No JavaScript — steps(1,end) timing snaps the block thumb in a single frame, and :active pushes the card into its hard offset shadow. */
```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: Brutalist Snap Toggle
Source: https://codefronts.com/components/css-toggles-switches/brutalist-snap-toggle/
Anti-animation as a statement: a raw, square switch with 3px black borders and a hard offset shadow. The block thumb doesn't glide — it SNAPS to the other side in a single 90ms step, the shadow jumps, and the state stamps itself in uppercase mono. Zero easing, zero apology.
## HTML
```html
<section class="tgl-17" aria-label="Brutalist snap toggle">
<label class="tgl-17__card" for="tgl-17-sw">
<span class="tgl-17__lbl">NEWSLETTER</span>
<span class="tgl-17__unit">
<input type="checkbox" id="tgl-17-sw" class="tgl-17__sw" role="switch">
<span class="tgl-17__state" aria-hidden="true"><i>OFF</i><i>ON</i></span>
</span>
</label>
<p class="tgl-17__cap">steps(1) — it snaps, it does not glide</p>
</section>
```
## CSS
```css
.tgl-17,
.tgl-17 *,
.tgl-17 *::before,
.tgl-17 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.tgl-17 {
font-family: ui-monospace,'SF Mono',Menlo,monospace;
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 30px;
padding: 60px 20px;
background: #f0ede6;
}
.tgl-17__card {
display: flex;
align-items: center;
gap: 30px;
background: #fff;
border: 3px solid #000;
box-shadow: 6px 6px 0 #000;
padding: 22px 28px;
cursor: pointer;
transition: transform .09s steps(1,end),box-shadow .09s steps(1,end);
}
.tgl-17__card:active {
transform: translate(3px,3px);
box-shadow: 3px 3px 0 #000;
}
.tgl-17__lbl {
font-size: 15px;
font-weight: 700;
letter-spacing: .14em;
color: #000;
}
.tgl-17__unit {
position: relative;
display: block;
}
.tgl-17__sw {
appearance: none;
-webkit-appearance: none;
display: block;
width: 104px;
height: 48px;
border: 3px solid #000;
background: #fff;
cursor: pointer;
position: relative;
transition: background-color .09s steps(1,end);
}
.tgl-17__sw:checked {
background: #ffe600;
}
.tgl-17__sw::before {
content: "";
position: absolute;
top: 3px;
left: 3px;
width: 36px;
height: 36px;
background: #000;
transition: transform .09s steps(1,end);
}
.tgl-17__sw:checked::before {
transform: translateX(56px);
}
.tgl-17__state {
position: absolute;
inset: 3px;
display: block;
pointer-events: none;
font-size: 13px;
font-weight: 800;
letter-spacing: .1em;
color: #000;
}
.tgl-17__state i {
position: absolute;
top: 50%;
transform: translateY(-50%);
font-style: normal;
}
.tgl-17__state i:nth-child(1) {
right: 10px;
}
.tgl-17__state i:nth-child(2) {
left: 10px;
display: none;
}
.tgl-17__sw:checked ~ .tgl-17__state i:nth-child(1) {
display: none;
}
.tgl-17__sw:checked ~ .tgl-17__state i:nth-child(2) {
display: block;
}
.tgl-17__sw:focus-visible {
outline: 4px solid #000;
outline-offset: 4px;
}
.tgl-17__cap {
font-size: 12px;
letter-spacing: .1em;
color: #8a8578;
}
@media (prefers-reduced-motion: reduce) {
.tgl-17__card,
.tgl-17__sw,
.tgl-17__sw::before {
transition: none;
}
}
```
## JavaScript
```js
/* No JavaScript — steps(1,end) timing snaps the block thumb in a single frame, and :active pushes the card into its hard offset shadow. */
```How this works
Brutalist motion is about violence of state change: the thumb's transform uses steps(1,end) over 90ms, so there is exactly one frame of movement — a snap, not a slide. The card's offset shadow is a solid non-blurred box-shadow:6px 6px 0 #000, and pressing shifts the whole control INTO its shadow (translate 3px, shadow 3px), the classic brutalist button push.
Checked state floods the track with signal yellow and swaps the stamped OFF/ON labels — set in uppercase monospace, because type IS the decoration here. Everything is rectangles; border-radius does not appear once.
Make it yours
- Swap signal yellow for brutalist pink (#ff90e8) or acid green.
- Go full instant: replace the 90ms steps with transition:none.
- Thicken borders to 4px at larger sizes — keep shadow offset = 2× border.
- Add a stamped index number (01/) before the label for catalog styling.
Gotchas — read before shipping
- steps(1,end) means NO intermediate frames — that's the aesthetic; don't 'fix' it with easing.
- Hard shadows must have zero blur; a 1px blur reads as an error, not a style.
- High contrast is the accessibility win here — keep text/background at 7:1+ and the focus ring inverted, not softened.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 4+ | 5+ | 4+ | 4+ |
Borders, box-shadow and steps() timing — this works in effectively every browser ever shipped with CSS3.