Light

Switch

A control that allows the user to toggle between a checked and not checked state.

Spec · from metadata

When to use

  • Binary on/off settings (e.g., enable notifications)
  • Feature toggles that take effect immediately
  • Preferences that don't require form submission

When not to use

  • Selecting from multiple options — use RadioGroup instead
  • Toggling a toolbar action — use Toggle instead
  • Checkbox in a form that requires submission — use Checkbox instead

Variants

PropValuesDefaultDescription
sizedefaultsmdefaultControls switch dimensions. Default is 32x18px, sm is 24x14px.

Anti-patterns

Avoid<Switch />
Prefer<Field orientation="horizontal">
  <FieldLabel htmlFor="darkMode">Dark mode</FieldLabel>
  <Switch id="darkMode" />
</Field>

Switch must have an accessible label. Use Field + FieldLabel for proper association.

Avoid<form>
  <Switch />
  <Button type="submit">Save</Button>
</form>
Prefer<Switch onCheckedChange={(checked) => updateSetting(checked)} />

Switch implies immediate effect. For form submission workflows, use Checkbox instead.

Accessibility

  • Required ARIAaria-label (when no visible label)
  • Min touch target32px
  • Screen readerBuilt on Radix Switch. Announces checked/unchecked state. Renders as role='switch' with aria-checked.
  • ContrastChecked state uses bg-primary. Unchecked uses bg-input. Thumb color inverts based on state.

Token bindings

TokenCategoryUsage
primarycolorChecked background
inputcolorUnchecked background
input-bgcolorThumb color (light mode)
ringcolorFocus ring

Import

import { Switch } from "@timelycare/helix-ui"
import { Label } from "@timelycare/helix-ui"

Props

interface SwitchProps {
  checked?: boolean
  onCheckedChange?: (checked: boolean) => void
  disabled?: boolean
  className?: string
}

Structure

PartTailwind
Trackw-9 h-5 rounded-full px-0.5 (36×20px)
Track (off)bg-input border-2 border-transparent
Track (on)bg-primary border-2 border-transparent justify-end
Thumbsize-4 rounded-full bg-input-bg shadow-lg (16px)
Labeltext-sm font-medium leading-none text-foreground
Descriptiontext-sm font-normal leading-normal text-muted-foreground
Label wrapperflex flex-col gap-2 (8px)

Types

TypeContainerUse For
DefaultNoneInline toggle settings
Boxborder border-border rounded-lg p-4 bg-input-bg shadow-xsForm sections, grouped settings

Label Position (Side)

PositionLayout
LeftSwitch first, label after
RightLabel first, switch at end

States

StateTrack Styling
Offbg-input (thumb at start)
Onbg-primary justify-end (thumb at end)
Focusring-[3px] ring-ring/50
Disabledopacity-50 cursor-not-allowed

Common Patterns

Basic Switch

<div className="flex items-center gap-2">
  <Switch id="airplane-mode" />
  <Label htmlFor="airplane-mode">Airplane Mode</Label>
</div>

With Description (Side=Left)

<div className="flex items-start gap-3">
  <Switch id="notifications" />
  <div className="flex flex-col gap-2">
    <Label htmlFor="notifications" className="text-sm font-medium leading-none">
      Push Notifications
    </Label>
    <p className="text-sm text-muted-foreground">
      Receive push notifications when someone mentions you.
    </p>
  </div>
</div>

Side=Right (Label First)

<div className="flex items-center justify-between">
  <div className="flex flex-col gap-2">
    <Label htmlFor="marketing">Marketing emails</Label>
    <p className="text-sm text-muted-foreground">
      Receive emails about new products and features.
    </p>
  </div>
  <Switch id="marketing" />
</div>

Box Type

<div className="flex items-start gap-3 rounded-lg border p-4 bg-input-bg shadow-xs">
  <Switch id="analytics" />
  <div className="flex flex-col gap-2">
    <Label htmlFor="analytics">Analytics</Label>
    <p className="text-sm text-muted-foreground">
      Share usage data to help improve our product.
    </p>
  </div>
</div>

Controlled Switch

const [enabled, setEnabled] = useState(false)

<Switch checked={enabled} onCheckedChange={setEnabled} />

Accessibility

  • Built on Radix Switch — handles keyboard and focus automatically
  • Keyboard: Space to toggle on/off
  • Screen reader: announces checked state via aria-checked
  • Requires label association via htmlFor/id
  • Focus visible ring applied automatically

Gotchas

ProblemSolution
Label not clickableUse htmlFor matching Switch id
Alignment issuesUse items-center for single line, items-start with description
Box type missing shadowAdd shadow-xs to container

See Also


Last updated: February 9, 2026