A binary switch. 18 variants across State (Default / Pressed / Disabled) × Size (Large / Medium / Small) × isSelected (true / false). Vector track + thumb, all colors token-bound.
State (Default / Pressed / Disabled), and moved the booleans to lowercase true/false. Toggle now shares the same schema as Radio Button — State × Size × a selection boolean — and maps cleanly to native Toggle / Switch. Only Code Connect and the ARIA-role docs remain.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).
State (Default / Pressed / Disabled) × Size × isSelected (true/false), matching Radio Button. Pressed is folded into State rather than a stray boolean, and Disabled correctly omits Pressed. It now shares the full schema with Checkbox and Radio Button, including the isSelected boolean name.| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| Off / On | Yes | Yes | isSelected=false / true | Grey track + knob-left when off; brand #005CE5 track + knob-right when on. |
| Pressed | Yes | Yes | State=Pressed | Darker track for tap feedback. Composes with either isSelected. Derived at runtime on native, not a passed parameter. |
| Disabled | Yes | Yes | State=Disabled | Muted track/knob, tap blocked. Ships at both isSelected values; correctly has no Pressed pairing. |
| Focused | N/A | N/A | — | N/A on mobile — touch has no focus ring. |
- ARIA role not documented. The native switch role and the required-toggle error announcement are not annotated on the component, so engineers have no spec for the a11y wiring. C7 · Code Connect Linkability
- Code Connect mappings not registered. Schema is normalized and stable, so registration is unblocked — but the SwiftUI / Compose mappings are not yet wired and the native component does not exist. Snippets remain a Planned API. C7 · Code Connect Linkability
- v2.0: Size axis added —
Large/Medium/Small(48 / 40 / 32 wide), previously absent. (C2) - v2.0: Interaction states added — a
Stateaxis now covers Default / Pressed / Disabled where the old component had only on/off/disabled. (C5) - v2.1: Schema normalized to the Selection Control pattern —
isPressedfolded intoState(Default / Pressed / Disabled, matching Radio Button), and the selection boolean moved fromisActive=Yes/Noto lowercaseisActive=true/false. Disabled correctly omits Pressed. Same 18 variants, no illegal-combination gap. (C2) - v2.1: Divergence from the Checkbox / Radio Button schema closed on the structural axes — Toggle now shares
State×Size× selection-boolean, so it maps to nativeToggle/Switchthe same way. (C2) - v2.2: Selection boolean renamed
isActive→isSelectedacross all 18 variants — Toggle now uses the exact same name as Checkbox and Radio Button, so the three selection controls are fully interchangeable. (C2)
- Document the ARIA / switch role. Spell out the native switch role and the required-toggle error announcement in the handoff spec. A11y
- Register Code Connect mapping to
EBToggle. WireState,Size, andisSelected1:1 to the SwiftUIToggle/ ComposeSwitchAPI. Docs
- Normalize to the Selection Control schema. v2.1: Applied —
State(Default / Pressed / Disabled) ×Size× lowercaseisSelected, matching Radio Button. Pressed folded intoState; booleans lowercase. Property - Add the Size axis. v2.0: Applied — Large / Medium / Small. Property
- Add interaction states. v2.0: Applied — Pressed and Disabled now ship as
Statevalues. State - Promote Toggle - With Label to a real component. v2.0: Applied — the labeled variant is now its own component that nests this Toggle as an instance (see the Toggle with Label assessment). Composition
- Rename
isActive→isSelected. v2.2: Applied — aligns the selection boolean with Checkbox and Radio Button across all 18 variants. Rename
The "off" resting state. Gray track, white knob pinned left.
Off / unchecked state.
| Role | Token | Default |
|---|---|---|
| Track | toggle/color/default/inactive/bg-track | #C2CFE5 |
| Indicator | toggle/color/default/inactive/bg-indicator | #FFFFFF |
The "on" resting state. Brand-blue track, knob pinned right.
On / checked state.
| Role | Token | Default |
|---|---|---|
| Track | toggle/color/default/active/bg-track | #005CE5 |
| Indicator | toggle/color/default/active/bg-indicator | #FFFFFF |
Disabled off state. Muted gray track; interaction blocked.
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 state. Muted brand-blue track; interaction blocked.
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 |
| 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) |
| 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. |
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Ready | Track/knob layers cleanly named. |
| C2 | Variant & Property Naming | Requires Rework | Rename isSelected → 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. |
State × isSelected = 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 |
isSelected → isSelected; change values Yes/No → true/false. Open