{"slug":"toggle","name":"Toggle","packageName":"@liquidify/react","category":"controls","maturity":"experimental","summary":"A user-flippable control for a single boolean setting — the canonical glass switch.","importPath":"@liquidify/react/toggle","sourcePath":"packages/@liquidify/react/src/components/toggle/toggle.tsx","specPath":"packages/@liquidify/react/src/components/toggle/spec.md","docsPath":"../liquidify-page/content/component-source/toggle.md","documentationStatus":"draft","storyIds":["controls-toggle--default"],"additionalExports":["LiquidColor","LiquidTintValue","SizedControlProps","TintableControlProps","ToggleSize"],"anatomy":[],"props":[{"name":"checked","type":"boolean | undefined","required":false,"description":"Controlled checked state. Passing this switches Toggle to controlled mode: the rendered state always reflects this value and the consumer owns it via {@link ToggleProps.onChange}.","declaredIn":"packages/@liquidify/react/src/components/toggle/toggle.tsx"},{"name":"children","type":"ReactNode","required":false,"description":"Visible label content for the `\"button\"` variant (the iOS `.toggleStyle(.button)` analogue). When supplied with `variant=\"button\"`, this content renders inside the pill capsule — an icon glyph, text, or an icon+text pair — and the capsule sizes to it. Ignored — documented, not silent — for the default `\"switch\"` variant (a switch renders no visible text of its own). children role: `label` (charter §8).","declaredIn":"packages/@liquidify/react/src/components/toggle/toggle.tsx"},{"name":"className","type":"string | undefined","required":false,"description":"Optional additional CSS class merged into the glass surface class. The supported per-instance customisation seam: override any `--lq-*` custom property from this class's CSS — the token cascade the engine reads (ADR-0004). No raw inline style is involved, so this stays ADR-0003 clean.","declaredIn":"packages/@liquidify/react/src/liquid-component-props.ts"},{"name":"defaultChecked","type":"boolean | undefined","required":false,"description":"Uncontrolled seed for the internal checked state (ignored once {@link ToggleProps.checked} is supplied).","defaultValue":"`false`","declaredIn":"packages/@liquidify/react/src/components/toggle/toggle.tsx"},{"name":"disabled","type":"boolean | undefined","required":false,"description":"When `true`, sets the native `disabled` attribute on the `<button>`: the switch is removed from the tab order, all interaction is suppressed, and a `data-disabled` styling hook dims the surface. `aria-disabled` is NOT set — the native attribute provides full-suppression semantics.","declaredIn":"packages/@liquidify/react/src/components/toggle/toggle.tsx"},{"name":"indeterminate","type":"boolean | undefined","required":false,"description":"Tri-state DISPLAY overlay for `variant=\"checkbox\"` (the AppKit `NSButton.allowsMixedState` / `NSControl.StateValue.mixed` lineage) — a **web-only extension** with no SwiftUI `Toggle` equivalent. When `true` the box announces `aria-checked=\"mixed\"` (`data-state=\"mixed\"`) and paints a dash glyph, while the underlying value contract stays boolean: `checked` / `onChange` never carry `\"mixed\"`. Activating a mixed box commits a concrete `onChange(true)` (the WAI-ARIA APG default). Ignored — documented, not silent — under `\"switch\"` / `\"button\"` (a switch has no tri-state).","defaultValue":"`false`","declaredIn":"packages/@liquidify/react/src/components/toggle/toggle.tsx"},{"name":"labelProps","type":"LabelProps | undefined","required":false,"description":"Per-instance typography override for the `\"button\"`-variant label, forwarded to the internal {@link Text} (ADR-0028). Token-backed axes only; the HIG default is `variant=\"body\"` with `color=\"inherit\"`, so the label keeps the button's state colour (accent unchecked, on-fill checked, grey disabled). Applies only when {@link ToggleProps.children} is a string/number — an icon or icon+text element passes through unwrapped.","declaredIn":"packages/@liquidify/react/src/components/toggle/toggle.tsx"},{"name":"onChange","type":"((checked: boolean) => void) | undefined","required":false,"description":"Fires with the next boolean whenever the switch flips — pointer tap, drag past the midpoint, or `Space` / `Enter`. Fires at most once per effective flip, in both controlled and uncontrolled modes; a cancelled gesture, a drag settling back on its starting side, a repeated same-value request, or a disabled interaction emits nothing (the shared no-op policy).","declaredIn":"packages/@liquidify/react/src/components/toggle/toggle.tsx"},{"name":"onClick","type":"MouseEventHandler<HTMLButtonElement> | undefined","required":false,"description":"Documented native passthrough (charter §1): fires before Toggle's internal flip on every click event dispatched to the root — a pointer tap's trailing click, the browser's own click synthesis on `Space`, or the programmatic click Toggle dispatches on `Enter`. `event.preventDefault()` suppresses that click's flip **and** the resulting {@link ToggleProps.onChange}. A plain pointer tap defers its commit to this click event (rather than committing in the pointer handlers), and `Enter` routes through a single dispatched click, so cancellation is uniform across the pointer and keyboard (`Space` / `Enter`) paths. A drag settle (past the midpoint) is the one exception: it commits synchronously in `onPointerUp`, so its trailing click is swallowed rather than re-evaluated — documented as NOT click-cancellable. Either way `onClick` still fires exactly once per interaction (the synthesised-click swallow is preserved for drags). Toggle has no `onPress` — its activation IS the value change.","declaredIn":"packages/@liquidify/react/src/components/toggle/toggle.tsx"},{"name":"ref","type":"Ref<HTMLButtonElement> | undefined","required":false,"description":"Forwarded ref to the rendered element — the native `<button role=\"switch\">` track itself (charter §4 truthful ref; there is no wrapper element to diverge from this type).","declaredIn":"packages/@liquidify/react/src/components/toggle/toggle.tsx"},{"name":"size","type":"ToggleSize | undefined","required":false,"description":"Size of the switch — the `small · regular` subset of the canonical {@link SizedControlProps.sizesize scale} (the SwiftUI `.controlSize(_:)` analogue, ADR-0018). Resolved to geometry via `data-size` attribute selectors in `styles.css`; the root stamps `data-size` **unconditionally** (the Button pattern), so both values are always addressable. `mini` / `large` / `xlarge` are excluded — Apple ships no large switch.","defaultValue":"`\"regular\"`","declaredIn":"packages/@liquidify/react/src/components/toggle/toggle.tsx"},{"name":"tint","type":"LiquidTintValue | undefined","required":false,"description":"System-accent of the on-track (the SwiftUI `.tint(_:)` analogue), drawn from the restricted {@link LIQUID_COLORS } palette and applied via the `data-tint` attribute selector in CSS. Per Apple HIG a switch's \"on\" track is system green, so Toggle defaults to `\"green\"`. For `variant=\"button\"` the `tint` is the button accent — the checked fill and the label colour in both checked and unchecked — following the palette (default systemBlue). Disabled greys the text but keeps the checked tint fill. For `variant=\"checkbox\"` the HIG default is the app **`\"accent\"`** (system blue) — the checked/mixed box fill — diverging from the switch's green (open question O1, resolved: `\"checkbox\"` auto-overrides the green default unless an explicit `tint` is supplied). An explicit `tint` always wins.","defaultValue":"`\"green\"` (`\"accent\"` under `variant=\"checkbox\"`)","declaredIn":"packages/@liquidify/react/src/components/toggle/toggle.tsx"},{"name":"variant","type":"\"button\" | \"switch\" | \"checkbox\" | undefined","required":false,"description":"Visual style of the toggle. `\"switch\"` (default) renders the sliding-thumb switch; `\"button\"` renders a pill capsule (the SwiftUI `.toggleStyle(.button)` analogue) used for formatting toolbars and single-option on/off buttons — the whole capsule fills with the tint accent when on; `\"checkbox\"` renders a small rounded-square glass box (leading) with a trailing `children` label (the macOS-family `CheckboxToggleStyle` analogue) — no sliding thumb, no liquid-lens. The checkbox box carries `role=\"checkbox\"`, a structural state glyph (checkmark / dash / empty — ADR-0019), and defaults its `tint` to the app `\"accent\"` (system blue) per Apple HIG, diverging from the switch green.","defaultValue":"`\"switch\"`","declaredIn":"packages/@liquidify/react/src/components/toggle/toggle.tsx"}],"swiftUIMap":[{"swiftUI":"`isOn: Binding<Bool>`","react":"`checked` (controlled) / `defaultChecked` (uncontrolled seed) + `onChange(next: boolean)`","notes":"The controlled-value triad (ADR-0015); a `Binding` splits into the controlled `checked` + `onChange`, or the uncontrolled `defaultChecked`. `onChange` fires at most once per effective flip, in both modes — cancelled gestures, same-side settles, repeated same-value requests, and disabled interaction emit nothing (see `## Interaction`).","relationship":"direct"},{"swiftUI":"`.toggleStyle(.switch)` / `.toggleStyle(.button)` / `.toggleStyle(.checkbox)`","react":"`variant: \"switch\" | \"button\" | \"checkbox\"`","notes":"The style-protocol name is the value (charter §6); `\"switch\"` is the default sliding-thumb pill, `\"button\"` the pill capsule, `\"checkbox\"` the leading glass box + trailing label (`CheckboxToggleStyle`, macOS family — no iOS render).","relationship":"direct"},{"swiftUI":"`.tint(_:)`","react":"`tint`","notes":"Restricted `LiquidColor` palette; default `\"green\"` (HIG switch-on divergence from the base `tint=\"accent\"` default — documented in `## Tokens`). The `\"checkbox\"` variant's HIG default reads as **accent** (system blue), not green — auto-override vs. caller-set is open question O1.","relationship":"direct"},{"swiftUI":"`NSButton.allowsMixedState` / `NSControl.StateValue.mixed` (AppKit, `checkbox` only)","react":"`indeterminate` → `aria-checked=\"mixed\"` + dash glyph","notes":"**Web-only extension** — no SwiftUI `Toggle` equivalent. A controlled *display* overlay: the value contract stays boolean (`checked`/`onChange`), `indeterminate` only drives the mixed rendering + announcement. Activating a mixed box commits a concrete `onChange(true)` (APG default; the committed boolean is open question O3). Ignored — documented, not silent — under `\"switch\"`/`\"button\"`.","relationship":"web-only"},{"swiftUI":"`.controlSize(_:)`","react":"`size: ToggleSize` (`Extract<Size, \"small\" | \"regular\">`)","notes":"`mini`/`large`/`xlarge` excluded — Apple ships no large switch. `data-size` is stamped unconditionally on the root (Button pattern); default `\"regular\"`.","relationship":"direct"},{"swiftUI":"`.disabled(_:)`","react":"`disabled`","notes":"Shape N: native `disabled` + `data-disabled`; no `aria-disabled` (full-suppression semantics, charter §3).","relationship":"direct"},{"swiftUI":"`Toggle(_:isOn:) { label }` closure","react":"`children`","notes":"Visible label under `variant=\"button\"` and `variant=\"checkbox\"` (icon, text, or icon+text; trailing the box under `\"checkbox\"`); ignored — documented, not silent — under `\"switch\"` (charter §8 `children` role: label).","relationship":"direct"},{"swiftUI":"label-view modifiers (`.bold()`, …)","react":"`labelProps` (`Pick<TextProps, …>`)","notes":"Per-instance typography override forwarded to the internal `Text` (ADR-0028); token-backed axes only, applies only to string/number `children`.","relationship":"direct"},{"swiftUI":"`.accessibilityLabel(_:)` / `.labelsHidden()` (nameless switch)","react":"`aria-label` / `aria-labelledby`","notes":"Required accessible-name source under `variant=\"switch\"` — the control renders no text of its own. Under `\"button\"`/`\"checkbox\"` a text `children` label supplies the name, but an icon-only/empty label still requires an explicit `aria-label` / `aria-labelledby`.","relationship":"direct"},{"swiftUI":"— (web-only)","react":"`className`","notes":"Overridable glass-surface class (`LiquidBaseProps`, inherited).","relationship":"web-only"},{"swiftUI":"— (web-only)","react":"`ref: Ref<HTMLButtonElement>`","notes":"Forwarded to the native `<button role=\"switch\">` track — the charter-truthful ref target (charter §4).","relationship":"web-only"},{"swiftUI":"— (web-only)","react":"`onClick?: MouseEventHandler<HTMLButtonElement>`","notes":"Documented native escape hatch (charter §1): fires before the internal flip on every click event (pointer tap, Space); `event.preventDefault()` suppresses the flip and `onChange`. Toggle has no `onPress` — its activation IS the value change (charter §1 rule 4).","relationship":"web-only"}],"tokens":["--lq-color-accent","--lq-color-action-secondary","--lq-color-bg-primary","--lq-color-separator","--lq-color-system-green-default-dark","--lq-color-system-green-default-light","--lq-duration-","--lq-easing-","--lq-glass-","--lq-hit-target-touch","--lq-lens-progress","--lq-lens-x","--lq-radius-","--lq-radius-pill","--lq-shadow-ambient-","--lq-space-"],"keyboard":[{"key":"Keyboard","action":"Keyboard: switch/button — Space / Enter flip via the same sequence; Tab focuses; arrows do nothing. checkbox — Space toggles per the APG checkbox pattern; whether Enter also toggles (Toggle's cross-variant behaviour) or is dropped to match the APG letter (Space-only) is open question O2. Tab focuses; arrows do nothing (a lone checkbox is not a radiogroup — no roving tabindex)."}],"variants":["button","checkbox","switch"],"variantPages":[{"slug":"toggle-checkbox","variant":"checkbox","title":"Toggle (checkbox variant)","docsPath":"../liquidify-page/content/component-source/toggle-checkbox.md","documentationStatus":"stable"}]}