Skip to main content
Urbicon UI

Toggle

An on/off switch for a single setting.

Playground

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

01 Examples

Notification preferences

onCheckedChange receives the new boolean right after the user flips a switch, which is where persisting it belongs. It rides the input's change event, so a checked value you assign in code moves the switch without calling it.

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"
      />
    </div>
    <div class="py-3">
      <Toggle
        bind:checked={emailDigest}
        label="Email Digest"
        helper="Weekly summary of your activity"
      />
    </div>
    <div class="py-3">
      <Toggle
        bind:checked={readReceipts}
        label="Read Receipts"
        helper="Let others know when you've seen their messages"
      />
    </div>
  </div>
</div>

Submitted with a form

name submits the switch as value (on by default) for as long as it is on. An off switch stays out of the FormData altogether, so read the presence of the key rather than a true or false.

Submitted keys: nothing yet

<form class="flex w-full flex-col gap-3" onsubmit={handleSubmit}>
  <Toggle name="beta" label="Join the beta channel" />
  <Toggle name="telemetry" label="Share anonymous usage data" checked />
  <Button type="submit" size="sm" class="self-start">Save</Button>
  <p class="text-text-tertiary text-xs">
    Submitted keys: <code class="text-text-primary">{saved ?? 'nothing yet'}</code>
  </p>
</form>

Mint micro-interactions

The effect listens on the track, so it wants the pointer over the switch itself and stays quiet while the pointer is on the label text. A device without hover never sees it at all.
<Toggle mint="glow" label="Glow on hover" checked intent="success" />
<Toggle mint={['scale', 'glow']} label="Scale and glow together" checked intent="danger" />

02 Customization

Night-sky track

Track and thumb both carry a data-state of checked or unchecked, so the gradient hangs off the on-state instead of a ternary in your markup.
<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}
    aria-label="Dark mode"
    intent="neutral"
    size="lg"
    slotClasses={{
      track:
        'data-[state=checked]:border-transparent data-[state=checked]:bg-linear-to-r data-[state=checked]:from-indigo-600 data-[state=checked]:to-violet-700 data-[state=checked]:shadow-lg data-[state=checked]:shadow-indigo-500/30'
    }}
  />
  <MoonIcon size={20} class="text-text-secondary" />
</div>

If every switch in the app should share a track treatment, set it once as a defaults entry for Toggle on a BlocksProvider. A preset is the opt-in variant of the same thing: it reaches only the switches that name it through their preset prop.

This is one of five ways to restyle a block. See Customization for class, slotClasses, unstyled, preset and provider-level overrides.

03 Accessibility

Built-in ARIA

The input is a checkbox with role="switch" and an aria-checked that follows the state. A label names it, helper and error text reach it through aria-describedby, and an error sets aria-invalid. Where a design carries no visible text, the switch falls back to a translated generic name, so pass your own aria-label instead.

Keyboard

Tab to focus, Space to toggle. The focus ring shows for keyboard users only and sits on the track.

Reduced motion

Every Mint effect is switched off under prefers-reduced-motion, and the thumb slide collapses to a millisecond along with every other duration token.

Don't wrap with <label>

Toggle renders its own associated <label> around track and text, and HTML has no meaning for a second one wrapped around that. Give the switch its text through the label prop instead.

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';