Toggle

An on/off switch for binary settings and preferences. 48×28px, spring-animated thumb. Off is --ink-line; on is teal. Used in notification preferences, split defaults, and app settings. The spring motion gives it a physical, satisfying feel.

Variants

Standalone
With label (trailing)
Settings row (label left, toggle right)
GPay nudges Send UPI requests via GPay
WhatsApp reminders Send settle reminders over WhatsApp

Standalone is used when the toggle is embedded in a larger component that already provides context — a card, a form group, a modal.

With label is the default for most settings — the label trails the switch. Label color transitions from muted (off) to ink (on) to reinforce the state change.

Settings row puts the label on the left and the toggle on the right, which maps to the iOS Settings pattern users already know. Use this in the app's settings screen.

Sizes

Default — 48×28
Compact — 40×24
SizeTrackThumbTravelUse case
Default48×28px24px circle20pxSettings rows, preferences — most contexts
Compact40×24px20px circle16pxDense forms, inline within cards

States

Off
On
Disabled off
Disabled on
StateTrackThumb positionNotes
Off--ink-lineLeft (2px from edge)Default resting state
On--tealRight (translateX 20px)Thumb springs to position
Disabled off--ink-line at 40% opacityLeftCursor: not-allowed. Add aria-disabled="true".
Disabled on--teal at 40% opacityRightSetting is locked on — explain why in surrounding text

On dark surfaces

On dark surfaces the off-track uses --dark-border and the thumb uses --dark-text (paper). On state is unchanged — teal is the same. Label text uses --dark-text-2 (off) and --dark-text (on).

Anatomy

Settle reminders
Track (48×28, teal when on)
Thumb (24px, paper, spring anim)
Label (optional, 15px/500)
PartElementNotes
Hidden input.toggle-input (checkbox)Visually hidden but present for native behavior, keyboard access, and form serialization.
Wrapper<label class="toggle">Clicking anywhere on the label toggles the input. Set cursor: pointer.
Track.toggle-track48×28px pill. Background transitions between --ink-line and --teal over 200ms spring.
Thumb.toggle-thumb24px circle, paper white, shadow-sm. Translates 20px to the right when checked.
Label.toggle-labelOptional. Can precede or follow the track depending on layout variant.

Usage

Do Use toggle for binary on/off settings that take immediate effect — no save button needed. Label should describe the on state: "Settle reminders" not "Toggle settle reminders". Use the settings row layout in the app settings screen.
Don't Don't use toggle for choices that need a save step — use radio buttons or a confirm button pattern instead. Don't use toggle for non-binary states. Don't label the toggle "On/Off" — describe what the setting does.
Do Group related toggles in a settings section with a heading. The sublabel can explain consequences: "Send UPI requests via GPay" clarifies what enabling actually does.
Don't Don't use the compact size as the default — only use it when space is genuinely constrained. Don't remove the spring animation — it's what makes the toggle feel physical rather than digital.

Accessibility

Semantics Uses a native <input type="checkbox"> under the hood — screen readers announce it as a switch. Add role="switch" to the input for semantic accuracy: <input type="checkbox" role="switch">. aria-checked is managed automatically.
Keyboard Focusable via Tab. Space toggles the state. Focus ring appears on the track via :focus-visible: 2px solid var(--teal), 2px offset. Never remove the focus ring.
Disabled state Use the native disabled attribute — it removes focusability automatically. If the toggle is locked by a permission (not disabled by logic), use aria-disabled="true" and keep it focusable so screen reader users can encounter it. Include an explanation in the sublabel.

Code

HTML

<!-- Default toggle with label -->
<label class="toggle">
  <input class="toggle-input" type="checkbox" role="switch">
  <div class="toggle-track"><div class="toggle-thumb"></div></div>
  <span class="toggle-label">Settle reminders</span>
</label>

<!-- Settings row -->
<div class="toggle-row">
  <div class="toggle-label-group">
    <span class="toggle-label">GPay nudges</span>
    <span class="toggle-sublabel">Send UPI requests via GPay</span>
  </div>
  <label class="toggle">
    <input class="toggle-input" type="checkbox" role="switch" checked>
    <div class="toggle-track"><div class="toggle-thumb"></div></div>
  </label>
</div>

<!-- Disabled -->
<label class="toggle disabled">
  <input class="toggle-input" type="checkbox" role="switch" disabled>
  <div class="toggle-track"><div class="toggle-thumb"></div></div>
  <span class="toggle-label">Locked setting</span>
</label>

Design tokens used

TokenValueRole
--ink-line#DDD2B8Track — off state
--teal#0E7C66Track — on state
--s-paper#FBF8F0Thumb fill
--shadow-sm0 1px 2px rgba(23,21,19,.06)Thumb elevation
--ease-springcubic-bezier(0.34,1.56,0.64,1)Thumb travel + track color
--r-pill999pxTrack border radius