47 CSS Toggles & Switches26 / 47
Industrial Rocker I/O Switch
The power switch off the back of real hardware: a rectangular rocker with engraved I and O halves seesawing inside a riveted steel panel, plus a red pilot lamp that ignites on power. Perspective, gradients and text-shadow do all the machining.
Published
The code
<section class="tgl-26" aria-label="Industrial rocker power switch">
<div class="tgl-26__panel">
<span class="tgl-26__rivet" style="top:8px;left:8px" aria-hidden="true"></span><span class="tgl-26__rivet" style="top:8px;right:8px" aria-hidden="true"></span><span class="tgl-26__rivet" style="bottom:8px;left:8px" aria-hidden="true"></span><span class="tgl-26__rivet" style="bottom:8px;right:8px" aria-hidden="true"></span>
<span class="tgl-26__lamp" aria-hidden="true"></span>
<span class="tgl-26__well">
<input type="checkbox" id="tgl-26-sw" class="tgl-26__sw" role="switch" aria-label="Main power">
<label class="tgl-26__rocker" for="tgl-26-sw" aria-hidden="true"><i>I</i><i>O</i></label>
</span>
<span class="tgl-26__tag">MAIN PWR</span>
</div>
<p class="tgl-26__cap">IEC rocker · I = on, O = off</p>
</section><section class="tgl-26" aria-label="Industrial rocker power switch">
<div class="tgl-26__panel">
<span class="tgl-26__rivet" style="top:8px;left:8px" aria-hidden="true"></span><span class="tgl-26__rivet" style="top:8px;right:8px" aria-hidden="true"></span><span class="tgl-26__rivet" style="bottom:8px;left:8px" aria-hidden="true"></span><span class="tgl-26__rivet" style="bottom:8px;right:8px" aria-hidden="true"></span>
<span class="tgl-26__lamp" aria-hidden="true"></span>
<span class="tgl-26__well">
<input type="checkbox" id="tgl-26-sw" class="tgl-26__sw" role="switch" aria-label="Main power">
<label class="tgl-26__rocker" for="tgl-26-sw" aria-hidden="true"><i>I</i><i>O</i></label>
</span>
<span class="tgl-26__tag">MAIN PWR</span>
</div>
<p class="tgl-26__cap">IEC rocker · I = on, O = off</p>
</section>.tgl-26,
.tgl-26 *,
.tgl-26 *::before,
.tgl-26 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.tgl-26 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 28px;
padding: 60px 20px;
background: #1f2126;
}
.tgl-26__panel {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
padding: 34px 44px 22px;
border-radius: 10px;
background: repeating-linear-gradient(92deg,#585d66 0 2px,#4e535c 2px 4px);
box-shadow: 0 22px 46px -20px #000,inset 0 1px 0 rgba(255,255,255,.18),inset 0 -2px 4px rgba(0,0,0,.4);
}
.tgl-26__rivet {
position: absolute;
width: 9px;
height: 9px;
border-radius: 50%;
background: radial-gradient(circle at 35% 30%,#9aa0aa,#4a4f58);
box-shadow: inset 0 -1px 2px rgba(0,0,0,.6);
}
.tgl-26__lamp {
position: absolute;
top: 14px;
right: 22px;
width: 12px;
height: 12px;
border-radius: 50%;
background: #3d2426;
box-shadow: inset 0 1px 3px rgba(0,0,0,.8);
transition: background-color .2s,box-shadow .2s;
}
.tgl-26__panel:has(.tgl-26__sw:checked) .tgl-26__lamp {
background: oklch(0.62 0.22 25);
box-shadow: 0 0 8px oklch(0.62 0.22 25),0 0 20px oklch(0.62 0.22 25 / .5);
}
.tgl-26__well {
display: block;
width: 60px;
height: 112px;
border-radius: 7px;
background: #191b1f;
box-shadow: inset 0 3px 8px rgba(0,0,0,.85),inset 0 0 0 2px #101114;
padding: 5px;
perspective: 280px;
position: relative;
}
.tgl-26__sw {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
}
.tgl-26__rocker {
display: grid;
grid-template-rows: 1fr 1fr;
width: 100%;
height: 100%;
border-radius: 5px;
background: linear-gradient(180deg,#2c2f35,#3a3e46 50%,#22252a);
cursor: pointer;
transform: rotateX(-16deg);
transform-origin: center;
transition: transform .16s cubic-bezier(.5,1.5,.6,1),background .16s;
}
.tgl-26__sw:checked ~ .tgl-26__rocker {
transform: rotateX(16deg);
background: linear-gradient(0deg,#2c2f35,#3a3e46 50%,#22252a);
}
.tgl-26__rocker i {
display: grid;
place-items: center;
font-style: normal;
font-size: 17px;
font-weight: 800;
color: #878d99;
text-shadow: 0 -1px 1px rgba(0,0,0,.8),0 1px 1px rgba(255,255,255,.15);
}
.tgl-26__sw:checked ~ .tgl-26__rocker i:first-child {
color: #cdd3dd;
}
.tgl-26__sw:not(:checked) ~ .tgl-26__rocker i:last-child {
color: #cdd3dd;
}
.tgl-26__sw:focus-visible ~ .tgl-26__rocker {
outline: 3px solid oklch(0.7 0.15 220);
outline-offset: 5px;
}
.tgl-26__tag {
font-size: 10px;
font-weight: 700;
letter-spacing: .24em;
color: #20232a;
background: #b8bdc6;
padding: 3px 10px;
border-radius: 3px;
box-shadow: inset 0 1px 0 rgba(255,255,255,.6);
}
.tgl-26__cap {
font-size: 11px;
letter-spacing: .16em;
text-transform: uppercase;
color: #5a5f68;
}
@media (prefers-reduced-motion: reduce) {
.tgl-26__rocker {
transition: none;
}
}.tgl-26,
.tgl-26 *,
.tgl-26 *::before,
.tgl-26 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.tgl-26 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 28px;
padding: 60px 20px;
background: #1f2126;
}
.tgl-26__panel {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
padding: 34px 44px 22px;
border-radius: 10px;
background: repeating-linear-gradient(92deg,#585d66 0 2px,#4e535c 2px 4px);
box-shadow: 0 22px 46px -20px #000,inset 0 1px 0 rgba(255,255,255,.18),inset 0 -2px 4px rgba(0,0,0,.4);
}
.tgl-26__rivet {
position: absolute;
width: 9px;
height: 9px;
border-radius: 50%;
background: radial-gradient(circle at 35% 30%,#9aa0aa,#4a4f58);
box-shadow: inset 0 -1px 2px rgba(0,0,0,.6);
}
.tgl-26__lamp {
position: absolute;
top: 14px;
right: 22px;
width: 12px;
height: 12px;
border-radius: 50%;
background: #3d2426;
box-shadow: inset 0 1px 3px rgba(0,0,0,.8);
transition: background-color .2s,box-shadow .2s;
}
.tgl-26__panel:has(.tgl-26__sw:checked) .tgl-26__lamp {
background: oklch(0.62 0.22 25);
box-shadow: 0 0 8px oklch(0.62 0.22 25),0 0 20px oklch(0.62 0.22 25 / .5);
}
.tgl-26__well {
display: block;
width: 60px;
height: 112px;
border-radius: 7px;
background: #191b1f;
box-shadow: inset 0 3px 8px rgba(0,0,0,.85),inset 0 0 0 2px #101114;
padding: 5px;
perspective: 280px;
position: relative;
}
.tgl-26__sw {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
}
.tgl-26__rocker {
display: grid;
grid-template-rows: 1fr 1fr;
width: 100%;
height: 100%;
border-radius: 5px;
background: linear-gradient(180deg,#2c2f35,#3a3e46 50%,#22252a);
cursor: pointer;
transform: rotateX(-16deg);
transform-origin: center;
transition: transform .16s cubic-bezier(.5,1.5,.6,1),background .16s;
}
.tgl-26__sw:checked ~ .tgl-26__rocker {
transform: rotateX(16deg);
background: linear-gradient(0deg,#2c2f35,#3a3e46 50%,#22252a);
}
.tgl-26__rocker i {
display: grid;
place-items: center;
font-style: normal;
font-size: 17px;
font-weight: 800;
color: #878d99;
text-shadow: 0 -1px 1px rgba(0,0,0,.8),0 1px 1px rgba(255,255,255,.15);
}
.tgl-26__sw:checked ~ .tgl-26__rocker i:first-child {
color: #cdd3dd;
}
.tgl-26__sw:not(:checked) ~ .tgl-26__rocker i:last-child {
color: #cdd3dd;
}
.tgl-26__sw:focus-visible ~ .tgl-26__rocker {
outline: 3px solid oklch(0.7 0.15 220);
outline-offset: 5px;
}
.tgl-26__tag {
font-size: 10px;
font-weight: 700;
letter-spacing: .24em;
color: #20232a;
background: #b8bdc6;
padding: 3px 10px;
border-radius: 3px;
box-shadow: inset 0 1px 0 rgba(255,255,255,.6);
}
.tgl-26__cap {
font-size: 11px;
letter-spacing: .16em;
text-transform: uppercase;
color: #5a5f68;
}
@media (prefers-reduced-motion: reduce) {
.tgl-26__rocker {
transition: none;
}
}/* No JavaScript — the rocker seesaws rotateX(-16°→16°) in a perspective well while gradient shading flips; :has() ignites the pilot lamp. */
/* No JavaScript — the rocker seesaws rotateX(-16°→16°) in a perspective well while gradient shading flips; :has() ignites the pilot lamp. */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: Industrial Rocker I/O Switch
Source: https://codefronts.com/components/css-toggles-switches/industrial-rocker-i-o-switch/
The power switch off the back of real hardware: a rectangular rocker with engraved I and O halves seesawing inside a riveted steel panel, plus a red pilot lamp that ignites on power. Perspective, gradients and text-shadow do all the machining.
## HTML
```html
<section class="tgl-26" aria-label="Industrial rocker power switch">
<div class="tgl-26__panel">
<span class="tgl-26__rivet" style="top:8px;left:8px" aria-hidden="true"></span><span class="tgl-26__rivet" style="top:8px;right:8px" aria-hidden="true"></span><span class="tgl-26__rivet" style="bottom:8px;left:8px" aria-hidden="true"></span><span class="tgl-26__rivet" style="bottom:8px;right:8px" aria-hidden="true"></span>
<span class="tgl-26__lamp" aria-hidden="true"></span>
<span class="tgl-26__well">
<input type="checkbox" id="tgl-26-sw" class="tgl-26__sw" role="switch" aria-label="Main power">
<label class="tgl-26__rocker" for="tgl-26-sw" aria-hidden="true"><i>I</i><i>O</i></label>
</span>
<span class="tgl-26__tag">MAIN PWR</span>
</div>
<p class="tgl-26__cap">IEC rocker · I = on, O = off</p>
</section>
```
## CSS
```css
.tgl-26,
.tgl-26 *,
.tgl-26 *::before,
.tgl-26 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.tgl-26 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 28px;
padding: 60px 20px;
background: #1f2126;
}
.tgl-26__panel {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
padding: 34px 44px 22px;
border-radius: 10px;
background: repeating-linear-gradient(92deg,#585d66 0 2px,#4e535c 2px 4px);
box-shadow: 0 22px 46px -20px #000,inset 0 1px 0 rgba(255,255,255,.18),inset 0 -2px 4px rgba(0,0,0,.4);
}
.tgl-26__rivet {
position: absolute;
width: 9px;
height: 9px;
border-radius: 50%;
background: radial-gradient(circle at 35% 30%,#9aa0aa,#4a4f58);
box-shadow: inset 0 -1px 2px rgba(0,0,0,.6);
}
.tgl-26__lamp {
position: absolute;
top: 14px;
right: 22px;
width: 12px;
height: 12px;
border-radius: 50%;
background: #3d2426;
box-shadow: inset 0 1px 3px rgba(0,0,0,.8);
transition: background-color .2s,box-shadow .2s;
}
.tgl-26__panel:has(.tgl-26__sw:checked) .tgl-26__lamp {
background: oklch(0.62 0.22 25);
box-shadow: 0 0 8px oklch(0.62 0.22 25),0 0 20px oklch(0.62 0.22 25 / .5);
}
.tgl-26__well {
display: block;
width: 60px;
height: 112px;
border-radius: 7px;
background: #191b1f;
box-shadow: inset 0 3px 8px rgba(0,0,0,.85),inset 0 0 0 2px #101114;
padding: 5px;
perspective: 280px;
position: relative;
}
.tgl-26__sw {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
}
.tgl-26__rocker {
display: grid;
grid-template-rows: 1fr 1fr;
width: 100%;
height: 100%;
border-radius: 5px;
background: linear-gradient(180deg,#2c2f35,#3a3e46 50%,#22252a);
cursor: pointer;
transform: rotateX(-16deg);
transform-origin: center;
transition: transform .16s cubic-bezier(.5,1.5,.6,1),background .16s;
}
.tgl-26__sw:checked ~ .tgl-26__rocker {
transform: rotateX(16deg);
background: linear-gradient(0deg,#2c2f35,#3a3e46 50%,#22252a);
}
.tgl-26__rocker i {
display: grid;
place-items: center;
font-style: normal;
font-size: 17px;
font-weight: 800;
color: #878d99;
text-shadow: 0 -1px 1px rgba(0,0,0,.8),0 1px 1px rgba(255,255,255,.15);
}
.tgl-26__sw:checked ~ .tgl-26__rocker i:first-child {
color: #cdd3dd;
}
.tgl-26__sw:not(:checked) ~ .tgl-26__rocker i:last-child {
color: #cdd3dd;
}
.tgl-26__sw:focus-visible ~ .tgl-26__rocker {
outline: 3px solid oklch(0.7 0.15 220);
outline-offset: 5px;
}
.tgl-26__tag {
font-size: 10px;
font-weight: 700;
letter-spacing: .24em;
color: #20232a;
background: #b8bdc6;
padding: 3px 10px;
border-radius: 3px;
box-shadow: inset 0 1px 0 rgba(255,255,255,.6);
}
.tgl-26__cap {
font-size: 11px;
letter-spacing: .16em;
text-transform: uppercase;
color: #5a5f68;
}
@media (prefers-reduced-motion: reduce) {
.tgl-26__rocker {
transition: none;
}
}
```
## JavaScript
```js
/* No JavaScript — the rocker seesaws rotateX(-16°→16°) in a perspective well while gradient shading flips; :has() ignites the pilot lamp. */
```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: Industrial Rocker I/O Switch
Source: https://codefronts.com/components/css-toggles-switches/industrial-rocker-i-o-switch/
The power switch off the back of real hardware: a rectangular rocker with engraved I and O halves seesawing inside a riveted steel panel, plus a red pilot lamp that ignites on power. Perspective, gradients and text-shadow do all the machining.
## HTML
```html
<section class="tgl-26" aria-label="Industrial rocker power switch">
<div class="tgl-26__panel">
<span class="tgl-26__rivet" style="top:8px;left:8px" aria-hidden="true"></span><span class="tgl-26__rivet" style="top:8px;right:8px" aria-hidden="true"></span><span class="tgl-26__rivet" style="bottom:8px;left:8px" aria-hidden="true"></span><span class="tgl-26__rivet" style="bottom:8px;right:8px" aria-hidden="true"></span>
<span class="tgl-26__lamp" aria-hidden="true"></span>
<span class="tgl-26__well">
<input type="checkbox" id="tgl-26-sw" class="tgl-26__sw" role="switch" aria-label="Main power">
<label class="tgl-26__rocker" for="tgl-26-sw" aria-hidden="true"><i>I</i><i>O</i></label>
</span>
<span class="tgl-26__tag">MAIN PWR</span>
</div>
<p class="tgl-26__cap">IEC rocker · I = on, O = off</p>
</section>
```
## CSS
```css
.tgl-26,
.tgl-26 *,
.tgl-26 *::before,
.tgl-26 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.tgl-26 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 28px;
padding: 60px 20px;
background: #1f2126;
}
.tgl-26__panel {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
padding: 34px 44px 22px;
border-radius: 10px;
background: repeating-linear-gradient(92deg,#585d66 0 2px,#4e535c 2px 4px);
box-shadow: 0 22px 46px -20px #000,inset 0 1px 0 rgba(255,255,255,.18),inset 0 -2px 4px rgba(0,0,0,.4);
}
.tgl-26__rivet {
position: absolute;
width: 9px;
height: 9px;
border-radius: 50%;
background: radial-gradient(circle at 35% 30%,#9aa0aa,#4a4f58);
box-shadow: inset 0 -1px 2px rgba(0,0,0,.6);
}
.tgl-26__lamp {
position: absolute;
top: 14px;
right: 22px;
width: 12px;
height: 12px;
border-radius: 50%;
background: #3d2426;
box-shadow: inset 0 1px 3px rgba(0,0,0,.8);
transition: background-color .2s,box-shadow .2s;
}
.tgl-26__panel:has(.tgl-26__sw:checked) .tgl-26__lamp {
background: oklch(0.62 0.22 25);
box-shadow: 0 0 8px oklch(0.62 0.22 25),0 0 20px oklch(0.62 0.22 25 / .5);
}
.tgl-26__well {
display: block;
width: 60px;
height: 112px;
border-radius: 7px;
background: #191b1f;
box-shadow: inset 0 3px 8px rgba(0,0,0,.85),inset 0 0 0 2px #101114;
padding: 5px;
perspective: 280px;
position: relative;
}
.tgl-26__sw {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
}
.tgl-26__rocker {
display: grid;
grid-template-rows: 1fr 1fr;
width: 100%;
height: 100%;
border-radius: 5px;
background: linear-gradient(180deg,#2c2f35,#3a3e46 50%,#22252a);
cursor: pointer;
transform: rotateX(-16deg);
transform-origin: center;
transition: transform .16s cubic-bezier(.5,1.5,.6,1),background .16s;
}
.tgl-26__sw:checked ~ .tgl-26__rocker {
transform: rotateX(16deg);
background: linear-gradient(0deg,#2c2f35,#3a3e46 50%,#22252a);
}
.tgl-26__rocker i {
display: grid;
place-items: center;
font-style: normal;
font-size: 17px;
font-weight: 800;
color: #878d99;
text-shadow: 0 -1px 1px rgba(0,0,0,.8),0 1px 1px rgba(255,255,255,.15);
}
.tgl-26__sw:checked ~ .tgl-26__rocker i:first-child {
color: #cdd3dd;
}
.tgl-26__sw:not(:checked) ~ .tgl-26__rocker i:last-child {
color: #cdd3dd;
}
.tgl-26__sw:focus-visible ~ .tgl-26__rocker {
outline: 3px solid oklch(0.7 0.15 220);
outline-offset: 5px;
}
.tgl-26__tag {
font-size: 10px;
font-weight: 700;
letter-spacing: .24em;
color: #20232a;
background: #b8bdc6;
padding: 3px 10px;
border-radius: 3px;
box-shadow: inset 0 1px 0 rgba(255,255,255,.6);
}
.tgl-26__cap {
font-size: 11px;
letter-spacing: .16em;
text-transform: uppercase;
color: #5a5f68;
}
@media (prefers-reduced-motion: reduce) {
.tgl-26__rocker {
transition: none;
}
}
```
## JavaScript
```js
/* No JavaScript — the rocker seesaws rotateX(-16°→16°) in a perspective well while gradient shading flips; :has() ignites the pilot lamp. */
```How this works
The rocker is one element inside a perspective well, holding the I and O legends in a 2-row grid. Unchecked it tips rotateX(-16deg) (O pressed in); checked, rotateX(16deg) (I pressed in). The tipped half also darkens via a gradient flip — rotation plus shading is what makes a seesaw read instead of a slide.
Engraving is the classic 1px dark-above / light-below text-shadow pair. The panel is brushed steel (repeating-linear-gradient grain) with four radial-gradient rivet dots, and the pilot lamp is a :has()-lit sibling with a deep red glow.
Make it yours
- Legends: I/O is IEC standard, but ⏻ or RUN/STOP work — keep 1–4 characters.
- Increase the rock to ±22° for a chunkier throw.
- Green pilot lamp for run-state (red conventionally means fault/power).
- Add a 30ms thunk: navigator.vibrate on change (2 lines of JS).
Gotchas — read before shipping
- The tip angle needs perspective on the WELL, not the rocker, for the halves to foreshorten correctly.
- Engraved text-shadow flips with the light: dark shadow above on recessed half, light below on raised.
- Keep the whole rocker one hit target — separate I and O click zones break checkbox semantics.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 15.4+ | 121+ | 111+ |
Floor set by :has(), oklch() as this demo is written. The core technique itself goes back further — Chrome 105+.
3D transforms are universal; only the pilot lamp uses :has() and it degrades to always-off styling.
Techniques used in this demo
3D transforms (perspective + preserve-3d):has()MDN ↗role="" (ARIA)MDN ↗radial-gradient()MDN ↗grid-templateMDN ↗place-items / place-contentMDN ↗