Components › Forms & input › Choice › Switch

Switch

canon

On/off control that applies immediately.

Size sm (acrm !3911, AD 26 Sep 2026): 30×17, knob 13px, --shadow-xs, label var(--small) in --fg-secondary, gap --sp-2 — ONLY the secondary option in a dialog footer next to buttons (Další / Vytvořit další / Nevolat / Pozvánky); the full size would compete with the primary. Replaces the private .tk-more/.sw copy — migrated on 27 Sep 2026 (acrm !3950): TaskDialog „Další“, NewContactDialog „Vytvořit další“/„Nevolat“, NewMeetingForm „Pozvánky“/„Nahrávat“; .tk-more/.sw deleted, the app has ONE switch. Clickable label (acrm !3933, AD 26 Sep 2026): a click on the VISIBLE label toggles (not with labelHidden); focus stays on the role=switch button, onChange(on, event) keeps stopPropagation for clickable rows, no double toggle.

Used in 35 files · 16 areas · 52× — components/CheckMenu, components/assistant, components/datamart, components/mail, components/meetings, components/person …

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.mjs has 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), font 500 13px var(--font-sans), color var(--ink-800).
  • Track: button.acrm-switch 34×20px, border-radius: var(--radius-pill), background: var(--line-300), no border, transition: background 0.15s.
  • Knob: > i 16×16px white circle at 2px inset, box-shadow: var(--shadow-sm); .is-on moves it by translateX(14px) and fills the track with --accent.
  • State: .is-on on the button carries the visuals; the programmatic state is role="switch" + aria-checked.
  • Label: label, after the track by default. labelPosition="start" puts it before the track — only in the mail compose footer ("Sledovat", story Variants). labelHidden keeps it for screen readers only, when the name lives elsewhere in the row (settings list).
  • Accessible name: a string label is the name by itself, visible or hidden (since 13 Sep 2026, acrm MR !3470). Pass ariaLabel only 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). title is 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 disabled while it runs (TypyCiselTab disabled={busy}).
  • In settings lists keep the label on the left and the switch right-aligned in the row; give the switch ariaLabel with 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, .toggle class) — the track is fixed at 34×20px.
  • Hand-written .acrm-switch markup — 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.
  • 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