Skip to content

Switch

Form InputsSince 0.2.0
import { Switch } from '@absoluteui/components/switch';
import '@absoluteui/components/switch/style.css';

Using Angular? See Switch for Angular.

An on/off setting that takes effect immediately. Switch renders a native <input type="checkbox" role="switch"> restyled as a track, with a decorative thumb whose position shows the state. Switches have no required state: a switch is a setting, not a question. Validity is always valid unless a custom error is set, for example when saving the setting failed.

  • A binary setting that applies immediately, such as enabling notifications or dark mode.
  • Settings pages where each row toggles one feature.
  • A choice that is only applied when a form is submitted: use Checkbox.
  • A choice the user must confirm (“I agree”): use a required Checkbox.
  • More than two states: use Radio Group or Select.
import { Switch } from '@absoluteui/components/switch';
const host = document.createElement('div');
document.body.append(host);
const notifications = Switch.create(host, { label: 'Email notifications', description: 'Weekly summary of activity' });
host.addEventListener('aui-change', () => {
document.documentElement.dataset.notifications = String(notifications.checked);
});
import { Switch } from '@absoluteui/components/switch';
const host = document.createElement('div');
document.body.append(host);
const sync = Switch.create(host, { label: 'Sync across devices', checked: true });
async function save(on: boolean): Promise<void> {
const response = await fetch('/api/settings/sync', { method: 'PUT', body: JSON.stringify({ on }) });
if (!response.ok) {
sync.setChecked(!on);
sync.setCustomError('Could not save. Try again.');
sync.reportValidity();
}
}
host.addEventListener('aui-change', () => {
void save(sync.checked);
});
import { Switch } from '@absoluteui/components/switch';
import '@absoluteui/components/switch/style.css';
const host = document.querySelector<HTMLElement>('#dark-mode');
if (host) {
const dark = Switch.create(host, { label: 'Dark mode' });
host.addEventListener('aui-change', () => {
document.documentElement.dataset.auiTheme = dark.checked ? 'absolute-dark' : 'absolute-default';
});
}
import { Switch } from '@absoluteui/components/switch';
const host = document.createElement('div');
document.body.append(host);
Switch.create(host, { label: 'Email notifications', description: 'Weekly summary of activity' });
<div class="aui-field aui-switch" data-aui-control> (consumer host)
<div class="aui-switch__row">
<span class="aui-switch__control">
<input type="checkbox" role="switch" class="aui-switch__input">
<span class="aui-switch__thumb" aria-hidden="true">
</span>
<label class="aui-field__label aui-switch__label" for=input>Label</label>
</div>
<div class="aui-field__description"> (optional)
<div class="aui-field__error aui-validation-message"> (custom error, when shown)
</div>
Switch.create(host: HTMLElement, options: SwitchOptions): Switch

Global configuration key: switch (see Configuration).

Option Type Default Description
label string Visible label and accessible name; clicking it toggles the switch. Required.
hideLabel boolean false Visually hide the label; it remains the accessible name.
checked boolean false Initial on/off state; also restored by reset().
value string "on" Submitted value when on.
name string Form field name.
description string Help text referenced by aria-describedby.
disabled boolean false Disables the switch.
icons Partial<{ error: IconRef }> Icon slot overrides.
validationTiming 'blur' | 'input' | 'submit' "blur" When a custom error message first appears; afterwards it updates live. Globally configurable.
locale string "en-US" Message locale. Globally configurable. Inherited from common configuration.
Property Type Description
host HTMLElement Consumer host; its children are owned by the component.
input HTMLInputElement The native checkbox with role=switch.
checked boolean Current on/off state.
value string Submitted value when on.
name string | null Form field name.
label string Label text.
disabled boolean Disabled state.
validity FieldValidity Always valid unless a custom error is set.
dirty boolean State differs from the initial state.
touched boolean Focus has left the control at least once.
options Readonly<SwitchOptions> Current instance options.
Method Description
setChecked(checked: boolean): void Programmatic state (no events); clears an external custom error.
update(options: Partial<SwitchOptions>): void Merge and apply options.
reportValidity(): boolean Show a custom error now and return validity.
setCustomError(message: string | null): void External error, for example a failed save; cleared when the user toggles.
getFormValue(): string | null value when on, otherwise null.
focus(): void Focus the switch.
reset(): void Restore the initial state and clear dirty/touched/submitted state.
destroy(): void Remove rendering, listeners, registration, and subscriptions.
Event Detail Bubbles Cancelable Description
aui-change { checked: boolean; value: string } Yes No The user toggled the switch.

All rules live in the aui.components cascade layer, so application styles override them without specificity battles.

Class Purpose
aui-field aui-switch Host.
aui-switch--invalid | --disabled State modifiers on the host.
aui-field__label | __description | __error Shared field chrome.
aui-switch__row Track and label row.
aui-switch__control Positioning wrapper for the track and thumb.
aui-switch__input Native input, restyled as the track.
aui-switch__thumb Decorative thumb; its position shows the state.
aui-switch__label The label (also aui-field__label).
Slot Default icon
error alert-circle

Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.

--aui-border-width | --aui-color-border | --aui-color-border-strong | --aui-color-disabled-bg | --aui-color-error-fg | --aui-color-focus-ring | --aui-color-on-primary | --aui-color-primary | --aui-color-surface | --aui-color-surface-sunken | --aui-color-text | --aui-color-text-disabled | --aui-color-text-muted | --aui-duration-fast | --aui-easing-standard | --aui-focus-ring-offset | --aui-focus-ring-width | --aui-font-family-sans | --aui-font-size-md | --aui-font-size-sm | --aui-font-weight-medium | --aui-font-weight-regular | --aui-icon-size-sm | --aui-line-height-normal | --aui-line-height-tight | --aui-radius-pill | --aui-space-1 | --aui-space-2 | --aui-switch-height | --aui-switch-width | --aui-touch-target-min

type ValidationTiming = 'blur' | 'input' | 'submit';
interface SwitchIcons { error: IconRef }
interface SwitchChangeDetail { checked: boolean; value: string }

Pattern: Native <input type=“checkbox” role=“switch”> with a <label for> association (WAI-ARIA APG Switch).

Key Context Action
Tab / Shift+Tab Any Moves focus to and from the switch.
Space Switch focused Toggles on/off (native).
Element Role
input switch (explicit on a native checkbox; checked state maps to on/off)
thumb and icons none (aria-hidden=true)
Attribute Element Value
role input “switch”
aria-describedby input Help text and visible error message ids
aria-invalid input “true” only while a custom error message is shown

The native input (the track) is the focus target and shows a color-focus-ring outline on :focus-visible.

Criterion Level Note
1.3.1 Info and Relationships A
1.4.1 Use of Color A
1.4.3 Contrast (Minimum) AA
1.4.11 Non-text Contrast AA
2.1.1 Keyboard A
2.3.3 Animation from Interactions AAA
2.4.7 Focus Visible AA
2.5.3 Label in Name A
2.5.8 Target Size (Minimum) AA
4.1.2 Name, Role, Value A

Automated test results: Accessibility and compliance.

  • The state is shown by the thumb position as well as by color.
  • Clicking the label toggles the switch, extending the hit target.
  • Enter does not toggle (native checkbox behavior); Space does.
  • Switches have no required state; a failed save can be reported with setCustomError.
  • Screen-reader announcement of role=switch on/off has not yet been manually verified with NVDA, JAWS, and VoiceOver.

off, on, hover, focus-visible, disabled, disabled on, invalid (custom error shown).

From Trigger To
off click, label click, or Space on; aui-change
on click, label click, or Space off; aui-change
any focus leaves the host touched; a custom error is shown (blur timing)
invalid (custom error) user toggles custom error cleared
any reset() initial state; pristine, untouched
  • The state is shown by thumb position as well as color (WCAG 1.4.1).
  • The label is clickable, so the target is the whole row (WCAG 2.5.8).
  • The label should describe the setting and stay constant; do not change it to “On”/“Off”.
  • Reduced motion removes the thumb transition.
  • Manual screen-reader verification is pending (see Known limitations).

The track is fixed-size (switch-width by switch-height); the label wraps beside it. Help text and messages align with the label.

The thumb uses inset-inline-start, so “on” moves it to the inline end in both directions (right in LTR, left in RTL) without direction-specific rules.

Off: color-surface-sunken track with a color-border-strong boundary and thumb. On: color-primary track with a color-on-primary thumb. High-contrast themes widen border-width. In forced-colors mode the track uses Canvas/CanvasText when off and Highlight/HighlightText when on, and disabled uses GrayText, with forced-color-adjust: none.

configureAbsoluteUI({ components: { switch: { validationTiming: 'submit' } } });

Changes apply to existing instances immediately. validationTiming only affects when a custom error first appears.

Instance options override global and theme defaults. All options are dynamic.

icons.error (default alert-circle). The thumb is a CSS shape, not an icon.

All text uses textContent. CSP-safe: no inline styles or HTML strings.

No persistence, network access, or telemetry. With name, the native input submits name=value when on.

Two listeners per instance (change, focusout), a shared document-level pointer tracker, and one configuration subscription. Target: under 1 ms per Switch.create; not yet covered by the performance test.

One value per instance. Not yet included in the 1,000-instance performance test.

  • reportValidity() returns true unless a custom error is set.
  • setChecked() clears a custom error, so revert the state first and then set the error when a save fails.
  • A disabled switch is not submitted natively, but getFormValue() still reports its value.
Code Category Cause
SWITCH_OPTIONS configuration Missing label or non-string value.
Validation (validity.error) validation custom only (set through setCustomError).

A failed construction leaves no rendering, listeners, or subscriptions behind.

  • No required or validate option by design; use a Checkbox for choices that must be confirmed.
  • No on/off text inside the track.
  • The track alone is 2.25rem by 1.25rem; the 24px target is met by the row and label.
  • Screen-reader behavior of role="switch" has not yet been manually verified with NVDA, JAWS, and VoiceOver.
  • A mixed/indeterminate state.
  • HTML inside the label.