Radio

Exclusive choice within a group. App-css sub with no React export: the canonical markup is div.acrm-radio-group[role="radiogroup"] > label.acrm-radio(.is-on) > input.acrm-radio-input + span.acrm-radio-dot + text (apps/web/src/ds/inputs.css §8; used in NastaveniPage and SouhlasPripojeniPage).

When to use

  • One choice out of 2–5 options where each option needs more text than a segment holds ("All companies" / "Customers only" / "Prospects only").
  • An option with a one-line description under the label (mail format, layout mode) — the dot aligns to the first line.
  • A form value that is saved with the form, not applied on click.

When not to use

  • 2–5 short labels or a view switch — use Segmented control (card Inputs: segmented for short labels and view switching, radio when an option needs more text).
  • More than ~5 options or a closed list — use Select (DS popover, never native <select>).
  • Independent options where several may be on — use Checkbox.
  • A bare <input type="radio"> without the DS classes — flagged by pnpm ds:validate:usage (WARN).

Anatomy

  • Group: .acrm-radio-group — flex-direction: column, gap: var(--sp-2), role="radiogroup" with aria-label or a <Field> label.
  • Option: label.acrm-radio — inline-flex, align-items: center, gap: var(--sp-3), font 500 13px var(--font-sans), color var(--ink-800), cursor: pointer.
  • Dot: .acrm-radio-dot 18×18px circle, border: 1.5px solid var(--border-strong); the inner 8px ::after is --accent and scales from 0 to 1 on .is-on (transition 0.12s); the border turns --accent too.
  • Input: .acrm-radio-input — real <input type="radio">, visually hidden (opacity: 0; width: 0; height: 0; position: absolute), carries name, checked, focus.
  • With description: align-items: flex-start on the label, margin-top: var(--sp-1) on the dot, description in var(--small) --fg-muted under the label text (NastaveniPage pattern).
  • Exception to the frame rule: the selected dot is a full accent indicator (card Inputs).

Best practices

Do

  • Keep .is-on in sync with the checked input — the CSS reads the class, not :checked.
  • One name per group and exactly one option checked; preselect the safe default.
  • Stack options vertically in the group; no horizontal radio rows.
  • Put the group under a <Field label> so the label names the whole choice.

Don't

  • A radio group with two options "Yes" / "No" — that is a Switch or a Segmented (2).
  • Mixing a radio group with checkboxes in one visual list.
  • Restyling the dot (size, colour) — 18px dot, 8px accent core, no variants.
  • Explanatory paragraphs under every option; the description line is one short sentence at most.

Content

  • Option labels in sentence case, noun phrases, parallel in form: "All companies", "Customers only".
  • Group label is a noun ("Scope", "Outgoing mail format").
  • Description under an option is one line, no trailing period needed: "Signature, logo and links".
  • Preselected option first when the order is not otherwise meaningful.
  • segmented-control — the compact form of the same exclusive choice for short labels.
  • checkbox — independent options.
  • select — exclusive choice from a longer closed list.
  • Stories: components--radio--default, components--radio--states, components--radio--composition.

Source: docs/choice/radio.usage.md · GitLab