Switch
On/off control that applies immediately. React <Switch> (packages/ui/src/components/Switch.tsx, @acrm/ui) renders the canonical markup span.acrm-switch-row > button.acrm-switch(.is-on)[role=switch][aria-checked] > i + label; the look comes from the app-side engine apps/web/src/ds/inputs.css §11. Since 12 Sep 2026 every switch in the app goes through it — the thirteen hand-written copies in nine files were migrated in acrm MR !3467.
When to use
- A setting that takes effect on click without a save button: "Offer in the dialer", "Show on the company card".
- Toggles in settings lists — label on the left, switch on the right, one row per setting.
- Inline options in a wizard step that change the preview at once ("First row is a header").
When not to use
- An option inside a form that is saved with the form — use Checkbox.
- A choice between two named values ("Month" / "Week", "User" / "Team") — use Segmented control.
- A
role="switch"with a custom class or a slider-like element — use.acrm-switch. (No validator rule enforces this today;validate-ds-usage.mjshas no switch check, so it rests on review.) - Bulk actions or navigation — a switch never triggers a process, it sets a state.
Anatomy
- Row:
.acrm-switch-row—inline-flex,align-items: center,gap: var(--sp-3), font500 13px var(--font-sans), colorvar(--ink-800). - Track:
button.acrm-switch34×20px,border-radius: var(--radius-pill),background: var(--line-300), no border,transition: background 0.15s. - Knob:
> i16×16px white circle at 2px inset,box-shadow: var(--shadow-sm);.is-onmoves it bytranslateX(14px)and fills the track with--accent. - State:
.is-onon the button carries the visuals; the programmatic state isrole="switch"+aria-checked. - Label:
label, after the track by default.labelPosition="start"puts it before the track — only in the mail compose footer ("Sledovat", storyVariants).labelHiddenkeeps it for screen readers only, when the name lives elsewhere in the row (settings list). - Accessible name: a string
labelis the name by itself, visible or hidden (since 13 Sep 2026, acrm MR !3470). PassariaLabelonly when the name must differ from the label — in a list it carries the item name ("Zapnout workflow X") — or when the label is not a string (mail compose footer).titleis available for a state tooltip. - Exception to the frame rule: the on-state is a full accent track — the switch has no frame variant.
Best practices
Do
- Apply the change on click and reflect it at once; if the write is async, set
disabledwhile it runs (TypyCiselTabdisabled={busy}). - In settings lists keep the label on the left and the switch right-aligned in the row; give the switch
ariaLabelwith the row's name. - In a clickable row, stop propagation in the handler —
onChange(on, event)receives the click event (WorkflowsSection). Enter and Space arrive as the same click. - Label the on-state as a positive statement: "Include inactive".
- Keep one switch per row; stacked rows use
gap: var(--sp-3).
Don't
- A switch that needs "Save" to take effect — that is a checkbox.
- "On" / "Off" text next to the switch — the track colour is the state.
- A page-specific toggle (custom track size, colour,
.toggleclass) — the track is fixed at 34×20px. - Hand-written
.acrm-switchmarkup — import<Switch>from@acrm/ui. None is left in the app (acrm MR !3467). - A switch to trigger an action ("Send", "Import") — use a button.
Content
- Label in sentence case, states what is on: "Send a weekly digest", not "Weekly digest on/off".
- No trailing period; verb or noun phrase, one line.
- Do not explain the consequence next to the switch; if something must be said, use the field hint under the group.
Related
checkbox— on/off inside a form.segmented-control— two or more named values.date-time-range— uses<Switch>for "Celý den" and the caller's "Opakovat".- Stories:
components--switch--default,components--switch--variants,components--switch--states,components--switch--composition.
Source: docs/choice/switch.usage.md · GitLab