This component is pure CSS — no JavaScript required.
# Switch — AGENTS.md
> Auto-generated from `switch.docs.md` + the modifier applicability matrix.
> Edit those source files; this file regenerates on build.
## Identity
- **Component:** `switch`
- **Layer:** elements
- **Status:** stable
- **Last updated:** 2026-05-19
## Summary
Use a switch when a setting takes immediate effect — turning on
## Variants
| Variant | Class | Use for |
|---------|---------------------|------------------------------------------|
| Default | `.switch` | Standard accent fill when on |
| Success | `.switch.-success` | Positive-state toggle (active / safe) |
| Danger | `.switch.-danger` | Risky state (mute / unsafe enable) |
| Small | `.switch.-s` | Compact size for dense UIs |
## Modifier applicability
Per ADR-0007. Modifiers attach via `data-{category}="value"` attributes.
| Category | Accepts |
|---|---|
| `background` | — |
| `surface` | — |
| `effect` | `glow` |
| `shader` | — |
| `rhythm` | `compact`, `regular` |
| `motion` | any value |
| `behavior` | — |
## Anatomy
```html
<label class="switch">
<input type="checkbox" class="switch__input" />
<span class="switch__track"></span>
<span class="switch__label">Enable notifications</span>
</label>
<!-- Pre-checked -->
<label class="switch -success">
<input type="checkbox" class="switch__input" checked />
<span class="switch__track"></span>
<span class="switch__label">Auto-save</span>
</label>
<!-- Disabled -->
<label class="switch">
<input type="checkbox" class="switch__input" disabled />
<span class="switch__track"></span>
<span class="switch__label">Coming soon</span>
</label>
```
The `<label>` wraps everything so the entire switch is clickable; the
`<input>` is visually hidden but accepts focus and keyboard input.
## Tokens consumed
### From semantic tier
- `--color-border-strong` — track off
- `--color-accent` — track on, focus ring (default)
- `--color-success` — track on (-success)
- `--color-danger` — track on (-danger)
- `--color-surface-canvas` — thumb
- `--color-ink-strong` — label text
- `--shadow-raised` — thumb shadow
- `--radius-pill` — track radius
- `--space-inset-element-s` — thumb inset from track edge
- `--space-gap-elements-m` — gap between track and label
- `--size-control-s` / `-m` — track size base
- `--border-width-hairline` / `--border-width-focus`
- `--font-body-regular-*` — label typography
- `--motion-fast` / `--motion-easing-default` — thumb slide
### Component-tier (defined inline)
- `--switch-track-w`, `--switch-track-h`, `--switch-thumb` —
overridable for custom sizing
## Accessibility contract
The native `<input type="checkbox">` carries all required semantics:
- Keyboard: Space toggles. Tab/Shift+Tab navigates.
- Screen readers announce "checkbox, checked/not checked" with the
label.
- The `<label>` wrapping provides the association — no explicit
`for` attribute needed.
- Focus appears on the track (via `:focus-visible` on the input + CSS
adjacent selector) because the input itself is visually hidden.
The component uses the standard "visually hidden" pattern for the
input. This is the recommended approach over `appearance: none` +
custom styling because it preserves every accessibility behavior of
the native control.
## Guidance
### Do
- Wrap the input in `<label class="switch">`.
- Use Switch for instant settings; use Checkbox for forms with submit.
- Pair with a concise label that reads as a setting name, not a
question ("Notifications", not "Do you want notifications?").
### Don't
- Don't use `display: none` on the input. It removes accessibility.
Use the visually-hidden pattern this component implements.
- Don't put two switches on one row without clear separation. They
are easily mistaken for siblings of one toggle.
- Don't bind a `click` handler to the track. The native `<input>`
fires `change` — listen there.