Skip to content
Component installs need the registry setup
VegaStack Design

Switch

An on/off toggle for instant, self-saving binary settings — two sizes, a choice-card layout, and a 24px hit area.

Status
stable
Since
0.1.0
Accessibility pattern
APG switch

Last updated

Install

Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Switch before installation. Retain the digest printed by this command.

pnpm exec vegastack-design verify switch

Install with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.

pnpm dlx shadcn@4.21.0 add @vegastack/switch

Run the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.

Usage

import { Switch } from "@/components/ui/switch";

<Switch />;

Examples

Description

Pair a Switch with FieldLabel and FieldDescription inside a horizontal Field when the setting needs a sentence of explanation.

Focus is shared across devices, and turns off when you leave the app.

Choice Card

Card-style selection, where FieldLabel wraps the whole Field so the entire card is clickable.

Disabled

Add disabled to the Switch, and data-disabled to the Field so the label dims with it.

Invalid

Add aria-invalid to the Switch, and data-invalid to the Field. The destructive border holds in every state, focused or not (FOC-14).

You must accept the terms and conditions to continue.

Size

size takes sm (24×14) and default (32×18.4). Both carry the same invisible 24px hit area.

RTL

The thumb translation is written with rtl: counterparts, so a right-to-left document moves the thumb the other way without any extra work.

Focus is shared across devices, and turns off when you leave the app.

يتم مشاركة التركيز عبر الأجهزة، ويتم إيقاف تشغيله عند مغادرة التطبيق.

States

Off, on and disabled, side by side.

Playground

Try both sizes with the disabled and invalid states, then copy the generated JSX.

<Field orientation="horizontal">
  <Switch id="notifications" />
  <FieldLabel htmlFor="notifications">Email notifications</FieldLabel>
</Field>

API Reference

PropTypeDefaultDescription
size"default" | "sm"—

Accessibility

  • Renders Base UI's Switch, which is a native <button role="switch"> carrying aria-checked; Enter and Space flip it.
  • The visible track is 14–18px tall, so an ::after pseudo-element extends the pointer target to at least 24×24 (A11Y-2), verified by a real elementFromPoint probe.
  • On :focus-visible the global 2px outline in base.css applies (FOC-1). The recipe carries no ring of its own and no outline-none (FOC-6).
  • A Switch has no visible text of its own — give it an aria-label, or a FieldLabel bound with htmlFor, as every example here does.
ContractStates tested
Behaviourdefault, checked, disabled, invalid
Accessibilitynative-or-base-ui-semantics, browser-accessibility-test, checked, invalid, disabled
Visualdefault, checked, invalid, disabled

Do / Don't

Do
Use a Switch for a setting that applies the moment it is flipped.
Don't
Use a Switch inside a form that needs a Submit step — that is a Checkbox.

Deviations

Upstream's file plus packages/ui/upstream/patches/switch.patch. Every hunk:

  • FOC-1, FOC-6 — the outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 glow is removed, along with the aria-invalid halo rings. base.css owns the single :focus-visible outline, and outline-none would suppress it.
  • FOC-14 — the invalid tint stays upstream's aria-invalid:border-destructive, held in every state, focused or not (FOC-14 retired FOC-5's not-focus: guard).
  • FOC-12 — upstream rings the whole choice card and cancels the control's own ring with group-has-[:focus-visible]/field-label:border-transparent and :ring-0. This system rings the control, so both counter-rules are removed with the card ring they existed to serve.
  • DOC-2 — cn is imported from @vegastack/design.
  • A11Y-24 — the invisible hit area grows to at least 44px tall on a coarse pointer. Widened by MK, 2026-10-26.

On this page