Switch — accessibility
Keyboard
- Native
<button>: Tab moves focus, Enter and Space toggle. disabledremoves it from the tab order (used while an async write runs).- No shortcut chip exists for the primitive.
ARIA
- One pattern everywhere:
role="switch"+aria-checked={on}onbutton.acrm-switch. Announced as "switch, on/off". - Settled 10 Sep 2026 (AD). The React
<Switch>used to reportaria-pressed("toggle button, pressed") while eleven of the thirteen hand-written copies reportedrole="switch"(the two on DsInputsPage had no ARIA at all) — one visual control saying different things to a screen reader, and "pressed button" describes an action, not a held state. The component was moved to the majority (acrm MR !3307). Since 12 Sep 2026 no copy is left: every switch renders through<Switch>(acrm MR !3467). - Accessible name: the visible
labelis not linked to the button, so the component copies a stringlabelintoaria-labelitself — visible or hidden;ariaLabeloverrides it. Until 13 Sep 2026 this happened only withlabelHidden, so a switch with a visible label and noariaLabelhad no name at all ("Celý den" and "Opakovat" in the meeting dialog). Fixed in the component, not per call site (acrm MR !3470), with a regression test that queries both bygetByRole('switch', { name }). - A non-string
label(mail compose footer, "Sledovat" in a<span>) still yields no name unlessariaLabelortitleis passed; that switch hastitle. .is-onis visual only; the state isaria-checked.- The knob
<i>is empty and needs noaria-hidden.
Focus
.acrm-switch:focus-visiblesetsoutline: noneandbox-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent)on the track (the foundation ring for[role='switch']inds.cssis overridden by this rule).- The ring follows the pill radius and does not change the track colour.
Contrast
Computed from packages/ui/src/tokens/tokens.css (WCAG 2.x relative luminance):
- On track
--accent#0072f5 on--surface-100#ffffff = 4.44:1 (light); on #1b1c1f = 3.83:1 (dark) — above 3:1. - Off track
--line-300#dcdbdd on white = 1.38:1 (light), #3a3c42 on #1b1c1f = 1.55:1 (dark) — below the 3:1 non-text minimum. - Knob white on off track = 1.38:1 (light), 11.02:1 (dark); knob white on on track = 4.44:1.
- Label
--ink-800#1f2023 on white = 16.29:1 (light), #e1e2e4 on #1b1c1f = 13.14:1 (dark). - Focus ring 30% accent mix is decorative.
Known limits
- Off-state track (1.38:1 light) and knob edge are below 3:1; the state is readable mainly by knob position and the accent fill when on. A
--border-stronghairline on the track would fix it — not in code. - No disabled style in
inputs.css;disabledworks for AT and keyboard but the track keeps full opacity. - The row text is not programmatically the label; the name is a copy in
aria-label, so a screen reader that shows text-to-name links sees none. Wrapping row and text in a<label>would fix it — not in code. - Knob and check colours are literal
#fffin CSS (not a token).
Source: docs/choice/switch.a11y.md · GitLab