A toggle for an immediately-applied binary setting. A real checkbox under the hood, zero JS.

Last updated

Show code
<Switch label="Enable notifications" />
<Switch label="Auto-renew" checked />

Add

npx @ava-ui/apogee add switch

No Apogee component dependencies. Zero JS.

Usage

---
import Switch from '@/components/apogee/Switch/Switch.astro'
---
<Switch id="notifications" name="notifications" label="Enable notifications" />

API

@composable — exposed to Content Editors in AVA. The Props interface is an open index signature ([key: string]: any), so any native <input> attribute not listed below (checked, disabled, name, value, required, aria-label, …) passes through to the underlying checkbox.

Prop Type Default Description
id string auto-generated (apogee-<random>) Connects the <label> to the <input>. Set your own to target it from elsewhere (a separate <label for>, a test selector).
label string — Visible label text, rendered after the track.
size 'small' | 'medium' | 'large' 'medium' Track and label size.
reversed boolean false Label first, switch last.
variant 'default' | 'slim' 'default' slim: a thin faded track with a bordered knob that overhangs it.
hasError boolean false Marks the switch invalid: aria-invalid on the input, a critical ring on the track.
hint string — Supporting text under the label; the input lists it in aria-describedby. The label turns medium weight to read as a heading.
class string — Extra classes on the root <label>, appended last.
…rest — — Any other attribute passes through to the <input type="checkbox" role="switch">.

Variants

Every variant:

Show code
<Switch label="Default" checked />
<Switch variant="slim" label="Slim" checked />
<Switch variant="slim" label="Slim, off" />

Sizes

Show code
<Switch size="small" label="Small" />
<Switch size="medium" label="Medium" />
<Switch size="large" label="Large" />

Hint

Show code
<Switch label="Two-factor authentication" hint="A code from your authenticator app on every sign-in." />

Reversed and slot label

Show code
<Switch reversed label="Email alerts" />
<Switch><span>Marketing <em>(optional)</em></span></Switch>

States

Show code
<Switch label="Off" />
<Switch label="On" checked />
<Switch label="Disabled" disabled />
<Switch label="Disabled, on" checked disabled />
  • Unchecked / checked — the track fill and thumb position flip on peer-checked.
  • Focus — a visible ring on the track when the hidden input has keyboard focus (peer-focus-visible:outline).
  • Disabled — the track takes the disabled fill (peer-disabled:bg-disabled); pass the native disabled attribute.
  • RTL — the thumb translates the other direction under a dir="rtl" ancestor.

Examples

No visible label — pass aria-label instead:

<Switch aria-label="Enable notifications" />

In a form, submitting a value when checked:

<Switch name="notifications" value="on" label="Enable notifications" />

Accessibility

  • A real <input type="checkbox" role="switch">, visually hidden (sr-only) but present in the tab order — full keyboard and AT support for free.
  • The <label for> wraps the whole control, so clicking the track or the label text both toggle it.
  • Keyboard — Tab to focus, Space to toggle (native checkbox behavior).
  • No visible label? Pass aria-label or aria-labelledby via passthrough — without one the switch has no accessible name.
  • Zero JS — no no-JS fallback to note.

Tokens

Token Used for
--background-neutral Track background (unchecked)
--background-disabled Track background (disabled)
--background-primary Track background (checked)
--outline-color-focus Focus ring

Do / Don’t

  • Do use Switch for a setting that applies immediately (e.g. “Auto-renew”).
  • Do pass a label or aria-label — never ship a switch with no accessible name.
  • Don’t use Switch for an action that needs a confirm step — that’s a Button.
  • Don’t pair a Switch with a separate “Save” button for that one setting — if it applies instantly, say so in nearby copy instead.

Content

  • Label states the setting being turned on, not the action (“Auto-renew”, not “Toggle auto-renew”).
  • Sentence case, no ending punctuation.