Skip to main content
Urbicon UI

Toggle

Accessible on/off switches with labels, sizes, intent-based styling, and micro-interactions.

Playground

Push updates instantly
Variant
Size Style variant (tailwind-variants)
Tier
Mint
<Toggle
  checked
  helper="Push updates instantly"
  label="Enable notifications"
/>

01 Examples

Notification Preferences

A realistic settings card with interactive toggles — the canonical pattern for preferences, account settings, and feature flags.

Notification Preferences

Manage how you receive updates

Receive alerts on your device
Weekly summary of your activity
Let others know when you've seen their messages
<div
  class="bg-surface-elevated border-border-subtle w-full overflow-hidden rounded-2xl border"
>
  <div class="border-border-subtle border-b px-5 py-3">
    <h3 class="text-text-primary text-sm font-semibold">Notification Preferences</h3>
    <p class="text-text-tertiary text-xs">Manage how you receive updates</p>
  </div>
  <div class="divide-border-subtle divide-y px-5">
    <div class="py-3">
      <Toggle
        bind:checked={notifications}
        label="Push Notifications"
        helper="Receive alerts on your device"
        intent="primary"
      />
    </div>
    <div class="py-3">
      <Toggle
        bind:checked={autoSave}
        label="Email Digest"
        helper="Weekly summary of your activity"
        intent="primary"
      />
    </div>
    <div class="py-3">
      <Toggle
        bind:checked={readReceipts}
        label="Read Receipts"
        helper="Let others know when you've seen their messages"
        intent="primary"
      />
    </div>
  </div>
</div>

Mint micro-interactions

A switch is worth a little motion feedback, because its two states look alike. Pass one effect by name, or an array to layer several — the array form is what the Playground's single-value Mint control cannot express. Everything here is suppressed under prefers-reduced-motion.
<Toggle mint="scale" label="Scale on hover" checked />
<Toggle mint="glow" label="Glow on hover" checked intent="success" />
<Toggle mint={['scale', 'glow']} label="Combined scale + glow" checked intent="danger" />

02 Customization

Gradient Tracks

Override the track slot with custom gradients for brand-specific controls.
<Toggle
  checked
  label="Premium Mode"
  slotClasses={{
    track:
      'bg-linear-to-r from-violet-500 to-fuchsia-500 shadow-lg shadow-violet-500/25 border-transparent'
  }}
/>
<Toggle
  checked
  label="Eco Mode"
  slotClasses={{
    track:
      'bg-linear-to-r from-emerald-500 to-teal-400 shadow-lg shadow-emerald-500/25 border-transparent'
  }}
/>
<Toggle
  checked
  label="Sunset Mode"
  slotClasses={{
    track:
      'bg-linear-to-r from-orange-500 to-rose-500 shadow-lg shadow-orange-500/25 border-transparent'
  }}
/>

Dark Mode Switch

A realistic dark mode toggle with icon-like styling.
<div
  class="bg-surface-elevated border-border-subtle inline-flex items-center gap-4 rounded-xl border px-5 py-3"
>
  <SunIcon size={20} class="text-text-secondary" />
  <Toggle
    bind:checked={darkMode}
    intent="neutral"
    size="lg"
    slotClasses={{
      track: darkMode
        ? 'bg-linear-to-r from-indigo-600 to-violet-700 shadow-lg shadow-indigo-500/30 border-transparent'
        : ''
    }}
  />
  <MoonIcon size={20} class="text-text-secondary" />
</div>

Fully Custom (unstyled)

Strip all defaults and rebuild with a monospace terminal aesthetic. Uses data-state for conditional styling.
<Toggle
  unstyled
  checked
  label="SYSTEM_ACTIVE"
  slotClasses={{
    control:
      'inline-flex cursor-pointer items-center gap-3 font-mono text-sm text-emerald-400 select-none',
    track:
      'relative h-6 w-12 rounded border border-emerald-500/50 bg-emerald-950/50 transition-colors data-[state=checked]:bg-emerald-500/20 data-[state=checked]:border-emerald-400',
    thumb:
      'absolute left-0.5 top-1/2 -translate-y-1/2 h-4.5 w-4.5 rounded-sm bg-emerald-500 transition-all data-[state=checked]:translate-x-6 data-[state=checked]:shadow-[0_0_12px_rgba(16,185,129,0.6)]'
  }}
/>
<Toggle
  unstyled
  label="NETWORK_IO"
  slotClasses={{
    control:
      'inline-flex cursor-pointer items-center gap-3 font-mono text-sm text-emerald-300 select-none',
    track:
      'relative h-6 w-12 rounded border border-emerald-500/30 bg-emerald-950/30 transition-colors data-[state=checked]:bg-emerald-500/20 data-[state=checked]:border-emerald-400',
    thumb:
      'absolute left-0.5 top-1/2 -translate-y-1/2 h-4.5 w-4.5 rounded-sm bg-emerald-500/40 transition-all data-[state=checked]:translate-x-6 data-[state=checked]:bg-emerald-500 data-[state=checked]:shadow-[0_0_12px_rgba(16,185,129,0.6)]'
  }}
/>

A brand track treatment reused across settings belongs in a BlocksProvider preset (presets.Toggle), applied via preset — see Customization.

03 Accessibility

Built-in ARIA

Renders with role="switch" and aria-checked that updates automatically. Labels are associated via id, and helper text is linked through aria-describedby.

Keyboard

Tab to focus, Space to toggle. The focus ring uses peer-focus-visible: to relay the hidden input's focus state onto the visible track.

Reduced Motion

The thumb slide animation and all Mint effects are suppressed when prefers-reduced-motion is enabled.

Don't wrap with <label>

Toggle already renders a correctly associated <label> internally. Wrapping it in another <label> creates nested label semantics — clicks on the outer label may not toggle the switch reliably across browsers, and screen readers can announce the label twice.

Don't

<label>
  Notifications
  <Toggle />
</label>

Do

<Toggle label="Notifications"
        helper="Email + push" />

04 API Reference

22 props
22 props
Prop
Type
Default
Description

05 Types

Local type definitions used by this component.

8 types
Name
Kind
Category
Used by
Description

06 Installation

Import

import { Toggle } from '@urbicon-ui/blocks';