Toggle Switch
Camera-style SPDT slide switch with detent mechanism
Size
Material
State
<div class="toggle-wrap" data-toggle>
<div class="toggle-track">
<div class="toggle-thumb"></div>
</div>
<span class="toggle-label">OFF</span>
</div>
<!-- ON state -->
<div class="toggle-wrap" data-toggle>
<div class="toggle-track on">
<div class="toggle-thumb"></div>
</div>
<span class="toggle-label">STAB</span>
</div> .toggle-track {
width: 46px; height: 22px; border-radius: 11px;
background: var(--bg-inset); border: 1px solid var(--border-subtle);
position: relative; transition: background 0.2s, border-color 0.2s;
box-shadow: inset 0 1px 4px rgba(0,0,0,0.4);
}
.toggle-track.on { background: #0d1a0d; border-color: #1a4a1a; }
[data-theme="light"] .toggle-track.on { background: #d0f0d0; border-color: #88cc88; }
.toggle-thumb {
position: absolute; top: 2px; left: 2px;
width: 16px; height: 16px; border-radius: 50%;
background: linear-gradient(155deg, #585858, #2a2a2a);
box-shadow: 0 1px 3px rgba(0,0,0,0.5), inset 0 1px 0 #686868;
transition: left 0.15s var(--snap-fast), background 0.2s, box-shadow 0.2s;
}
[data-theme="light"] .toggle-thumb {
background: linear-gradient(155deg, #eee, #ccc);
box-shadow: 0 1px 3px rgba(0,0,0,0.15), inset 0 1px 0 #fff;
}
.toggle-track.on .toggle-thumb {
left: 26px;
background: linear-gradient(155deg, var(--green-hi), var(--green-on));
box-shadow: 0 1px 3px rgba(0,0,0,0.5), inset 0 1px 0 #aaff99, 0 0 8px var(--green-glow);
}
.toggle-label {
font-size: 8px; letter-spacing: 2px; color: var(--text-muted);
transition: color 0.2s; text-transform: uppercase;
font-family: var(--font-ui); font-weight: 500;
}
.toggle-track.on ~ .toggle-label { color: var(--green-on); }
.toggle-wrap {
display: flex; flex-direction: column; align-items: center;
gap: 6px; cursor: pointer;
} API
| Class | Type | Description |
|---|---|---|
.toggle-track | Base | Primary component class |
.md | Size | Medium (default) variant |
.chrome | Material | Chrome surface variant |
.on | State | On state |
Design Notes
Physical Analog
Reference devices: Nikon lens VR switch (ON/OFF), Sony Alpha AF/MF toggle, Canon EOS stabilizer switch. Mechanism: SPDT slide switch (Single Pole, Double Throw). Plastic or metal slider moves linearly along a track between two positions. Metal wiper contact bridges different PCB trace pairs. Ball-and-spring detent mechanism holds slider in each position with positive click.
Geometry
| Property | Value |
|---|---|
| Track | 46x22px, milled rectangular channel, 2-3mm deep |
| Thumb | 16px diameter, 4-5mm protruding above track |
| Travel | 24px (left: 2px to left: 26px) |
| Detent force | 100-200gf |
CSS Recipe
Track (Container)
.toggle-track {
width: 46px; height: 22px; border-radius: 11px;
background: var(--bg-inset); border: 1px solid var(--border-subtle);
position: relative; transition: background 0.2s, border-color 0.2s;
box-shadow: inset 0 1px 4px rgba(0,0,0,0.4);
}
Track ON State
.toggle-track.on { background: #0d1a0d; border-color: #1a4a1a; }
[data-theme="light"] .toggle-track.on { background: #d0f0d0; border-color: #88cc88; }
Thumb (Slider Knob)
.toggle-thumb {
position: absolute; top: 2px; left: 2px;
width: 16px; height: 16px; border-radius: 50%;
background: linear-gradient(155deg, #585858, #2a2a2a);
box-shadow: 0 1px 3px rgba(0,0,0,0.5), inset 0 1px 0 #686868;
transition: left 0.15s var(--snap-fast), background 0.2s, box-shadow 0.2s;
}
Thumb Light Theme
[data-theme="light"] .toggle-thumb {
background: linear-gradient(155deg, #eee, #ccc);
box-shadow: 0 1px 3px rgba(0,0,0,0.15), inset 0 1px 0 #fff;
}
Thumb ON State
.toggle-track.on .toggle-thumb {
left: 26px;
background: linear-gradient(155deg, var(--green-hi), var(--green-on));
box-shadow: 0 1px 3px rgba(0,0,0,0.5), inset 0 1px 0 #aaff99, 0 0 8px var(--green-glow);
}
Label
.toggle-label {
font-size: 8px; letter-spacing: 2px; color: var(--text-muted);
transition: color 0.2s; text-transform: uppercase;
font-family: var(--font-ui); font-weight: 500;
}
.toggle-track.on ~ .toggle-label { color: var(--green-on); }
Wrapper
.toggle-wrap {
display: flex; flex-direction: column; align-items: center;
gap: 6px; cursor: pointer;
}
HTML Structure
<div class="toggle-wrap" data-toggle>
<div class="toggle-track">
<div class="toggle-thumb"></div>
</div>
<span class="toggle-label">OFF</span>
</div>
<!-- ON state -->
<div class="toggle-wrap" data-toggle>
<div class="toggle-track on">
<div class="toggle-thumb"></div>
</div>
<span class="toggle-label">STAB</span>
</div>
Size Variants
No explicit size variants defined.
Material Variants
- Track: Recessed panel material
- Thumb OFF: Metallic gradient (dome catching light from upper-left)
- Thumb ON: Green gradient with green glow (embedded status LED)
Theme Behavior
- Dark OFF thumb:
linear-gradient(155deg, #585858, #2a2a2a)with strong shadow - Light OFF thumb:
linear-gradient(155deg, #eee, #ccc)with softer shadow - Dark ON track:
#0d1a0d(dark green) - Light ON track:
#d0f0d0(light green)
Constraints
- Thumb travel MUST be exactly
left: 2px(OFF) toleft: 26px(ON) -- 24px travel. - Detent snap MUST use
--snap-fasteasing (overshoot simulates detent spring). - ON state thumb MUST include green glow (
0 0 8px var(--green-glow)) for LED effect. - Track border-radius MUST be half of height (11px) for capsule shape.
- Inset shadow on track MUST be
rgba(0,0,0,0.4)-- shallow recess.
Accessibility
- Add
tabindex="0"androle="switch"witharia-checkedon toggle-wrap - Keyboard: Space to toggle
- Requires JS to toggle
.onclass on track
♿
Accessibility
- Element
- Use <button> with role="switch"
- Keyboard
- Space to toggle
- Focus
-
Visible focus indicator required. Use native browser focus ring or custom
:focus-visiblestyles.