A binary switch control with on, off, and disabled states.
Fix — normalize to the Selection Control schema
Rename
isActive → isSelected, change Yes/No values to true/false, expand states from 2 → 5 (Default, Pressed, Focused, Disabled, Error), add Small/Medium/Large sizes. Once normalized, Toggle sits alongside Checkbox and Radio Button under one shared schema and maps cleanly to native Toggle / Switch.In Context
Toggle appears in settings rows, form opt-ins, and any control that flips a single boolean. Usually paired with a label (see Toggle - With Label).
Live Preview
Properties (today)
isActive
State
Proposed (post-normalization)
size
DS Health
Reusable
Pass
Generic on/off switch usable anywhere a boolean needs a visual control.
Self-contained
Pass
Owns its track, knob, colors, and shadow tokens.
Consistent
Warn
isActive + Yes/No breaks the DS convention set by Checkbox (isSelected + true/false). Selection controls should share one schema.Composable
Partial
Drops into rows and forms fine, but Toggle - With Label is a frame not a component, limiting composition into list items and labeled form rows.
Behavior
| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Default · Off | Yes | Yes | State=Default, isActive=No | Gray track, knob left. |
| Default · On | Yes | Yes | State=Default, isActive=Yes | Brand track, knob right. |
| Pressed | N/A | N/A | Not built | Need darker track + scaled knob — critical feedback for tap. |
| Focused | N/A | N/A | Not built | 2px focus ring for keyboard / switch-control users. |
| Disabled · Off / On | Yes | Yes | State=Disabled | Muted track/knob, tap blocked. |
| Error | N/A | N/A | Not built | Needed when required toggle (e.g. "accept terms") is unset on submit. |
Open Issues
- Property schema diverges from Checkbox. Rename
isActive→isSelected, change valuesYes/No→true/false. Selection controls should share one schema. C2 · Variant & Property Naming - Missing interaction states. Only Default + Disabled built. Add Pressed, Focused, Error to match Checkbox's 5-state model. C5 · Interaction State Coverage
- Missing size axis. No Small/Medium/Large. Add to match Checkbox + Radio Button. C2 · Variant & Property Naming
- No Code Connect mapping. Blocked until schema normalizes. C7 · Code Connect Linkability
Design Recommendations
- Normalize to the Selection Control schema. Bring Toggle in line with Checkbox and Radio Button so all three share one property language:
isSelected: true | false(fromisActive: Yes | No) ×State: Default | Pressed | Focused | Disabled | Error(up from Default/Disabled only) ×Size: Small | Medium | Large(new axis). Variant count grows from 4 → 30, all covered by a clean 2 × 5 × 3 matrix. Property - Promote Toggle - With Label to a proper component with
label, optionaldescription, optionalhelper/errortext,requiredmarker, andplacement = leading | trailing. See Toggle - With Label. Composition - Consider a Loading state for async toggles (settings that sync to the server). Shows a spinner on the knob while the request is in flight — common in Material 3 and iOS 17. State
- Document the ARIA role. Natively, Toggle maps to the
switchrole witharia-checked = true | false. Screen readers say "on/off" instead of "checked/unchecked" — the correct affordance for a settings toggle. A11y
States
Default · Off
DES DEV
The "off" resting state. Gray track, white knob pinned left.
Properties
isActive
State
Properties
State Default
isActive No
Colors
Track #C2CFE5
Indicator #FFFFFF
Layout
Track size 48 × 24
Knob size 20 × 20
Knob inset 2px
Corner radius 12px (pill)
Typography
N/A No text
Inactive — Colors
Off / unchecked state.
| Role | Token | Default |
|---|---|---|
| Track | toggle/color/default/inactive/bg-track | #C2CFE5 |
| Indicator | toggle/color/default/inactive/bg-indicator | #FFFFFF |
Default · On
DES DEV
The "on" resting state. Brand-blue track, knob pinned right.
Properties
isActive
State
Properties
State Default
isActive Yes
Colors
Track #005CE5
Indicator #FFFFFF
Layout
Track size 48 × 24
Knob size 20 × 20
Knob inset 2px
Corner radius 12px (pill)
Typography
N/A No text
Active — Colors
On / checked state.
| Role | Token | Default |
|---|---|---|
| Track | toggle/color/default/active/bg-track | #005CE5 |
| Indicator | toggle/color/default/active/bg-indicator | #FFFFFF |
Disabled · Off
DES DEV
Disabled off state. Muted gray track; interaction blocked.
Properties
isActive
State
Properties
State Disabled
isActive No
Colors
Track #EEF2F9
Indicator #F6F9FD
Layout
Track size 48 × 24
Knob size 20 × 20
Knob inset 2px
Corner radius 12px (pill)
Typography
N/A No text
Disabled Inactive — Colors
Disabled toggle in the off position — desaturated track.
| Role | Token | Default |
|---|---|---|
| Track | toggle/color/disabled/inactive/bg-track | #E5EBF4 |
| Indicator | toggle/color/disabled/inactive/bg-indicator | #F6F9FD |
Disabled · On
DES DEV
Disabled on state. Muted brand-blue track; interaction blocked.
Properties
isActive
State
Properties
State Disabled
isActive Yes
Colors
Track #9BC5FD
Indicator #F6F9FD
Layout
Track size 48 × 24
Knob size 20 × 20
Knob inset 2px
Corner radius 12px (pill)
Typography
N/A No text
Disabled Active — Colors
Disabled toggle in the on position — muted brand fill.
| Role | Token | Default |
|---|---|---|
| Track | toggle/color/disabled/active/bg-track | #9BC5FD |
| Indicator | toggle/color/disabled/active/bg-indicator | #F6F9FD |
Property Mapping
| Figma Property | SwiftUI | Compose |
|---|---|---|
isActive: Yes | No | isSelected: true | false | @Binding var isOn: Bool |
State: Default | Disabled | State: Default | Pressed | Focused | Disabled | Error | Modifier: .disabled(true), .ebState(.error) |
| (no size axis) | Size: Small | Medium | Large | .controlSize(.small / .regular / .large) |
Accessibility
| Requirement | iOS | Android |
|---|---|---|
| Switch role | SwiftUI Toggle automatically applies the switch accessibility trait — VoiceOver says "on/off", not "checked/unchecked". | Material Switch applies Role.Switch semantics automatically. |
| Touch target | Minimum 44 × 44pt (pad the container — the 48×24 track alone is too small). | Minimum 48 × 48dp. |
| State announcement | VoiceOver announces "On" / "Off" as the value. | TalkBack announces "On" / "Off" as the state description. |
| Disabled | .disabled(true) blocks interaction; VoiceOver announces "dimmed". | enabled = false blocks click; TalkBack announces "disabled". |
| Focus (external keyboard / switch control) | iPad keyboards and Switch Control need a visible focus ring — must be added as part of the Focused state. | D-pad / keyboard focus indicator — must be added as part of the Focused state. |
Criteria Scorecard
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Ready | Track/knob layers cleanly named. |
| C2 | Variant & Property Naming | Requires Rework | Rename isActive → isSelected, values Yes/No → true/false; add Size axis. |
| C3 | Token Coverage | Ready | Track + knob + shadow bound to tokens. |
| C4 | Native Mappability | Ready | Maps 1:1 to SwiftUI Toggle / Material Switch. |
| C5 | Interaction State Coverage | Requires Rework | Missing Pressed, Focused, Error. Need full 5-state model. |
| C6 | Asset & Icon Quality | Not Applicable | No icons. |
| C7 | Code Connect Linkability | Not Mapped | Blocked until schema normalizes. |
Variants Inventory (4 total)
State × isActive = 4 variants today. Proposed: isSelected × State × Size = 30 variants.
| # | Node | State | isActive | Dimensions |
|---|---|---|---|---|
| 1 | 18482:36509 | Default | No | 48 × 24 |
| 2 | 18482:36512 | Default | Yes | 48 × 24 |
| 3 | 18482:36515 | Disabled | No | 48 × 24 |
| 4 | 18482:36518 | Disabled | Yes | 48 × 24 |
1.0.0 — April 2026Major
Initial Assessment · node 18482:36508
Verdict: Fix — Normalize to the shared Selection Control schema alongside Checkbox and Radio Button. Open
SchemaC2 — Property naming — Rename
C2isActive → isSelected; change values Yes/No → true/false. OpenC5 — States — Add Pressed, Focused, Error states. Add Small/Medium/Large size axis. Open
C5C7 — Code Connect — Blocked until schema normalizes. Open
C7