From a8d54b6697e77c54e5f4a43d00b7275b806494c7 Mon Sep 17 00:00:00 2001 From: David Gracia Date: Wed, 15 Jul 2026 10:28:40 -0600 Subject: [PATCH] feat(RadioGroup): add single-choice control (17th atom, D22) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RadioGroup + RadioGroupItem is the shadcn "RadioGroup" contract over Base UI's RadioGroup + Radio — role="radiogroup"/role="radio", arrow-key navigation, single selection, hidden form input, shared Button/Input/Checkbox/Switch focus ring. Completes the boolean/choice form-control family: Checkbox -> Switch -> Radio. - Compound (group + item), unlike the self-contained Checkbox/Switch: the selection semantics (single choice, arrow nav, submitted value, name) live on the group, matching shadcn + ARIA APG. shadcn naming (RadioGroupItem, not Radio); data-pharos-slot radio-group / radio-group-item. - Shared form-control conventions: label-less (Escuela 1, D11), no size axis. Error is a group concern -> aria-invalid on the group, every item shows the ring (.group[aria-invalid] .item), not per-control. - Ring-with-dot look, token-derived: circular 20px (spacing-5) control, ring darkens to neutral-900 on select (family "active" color) + inner dot (spacing-2). Dot is a span (Base UI mounts Indicator only when checked), no icon dep. - Stories (Default + WithDisabledItem + Disabled + Invalid), unit tests (9), NAMING-decisions D22, changeset minor. - Harness (rule #8): Settings form gains a "Theme" radio group (light/dark/system); settings.spec picks + enforces single selection + arrow nav -- 26 e2e specs. Verified: build + dist-types, typecheck, lint, unit (RadioGroup 9/9, 214 total), test:storybook a11y (106 stories, +4), build-storybook, e2e 26/26. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01AgnovjGivnndpV7Yzt6AmY --- .changeset/radio-group-atom.md | 13 +++ NAMING-decisions.md | 53 ++++++++++ examples/saas-demo/e2e/settings.spec.ts | 22 ++++ examples/saas-demo/src/pages/Settings.tsx | 37 +++++++ src/components/RadioGroup.module.css | 81 +++++++++++++++ src/components/RadioGroup.stories.tsx | 94 +++++++++++++++++ src/components/RadioGroup.tsx | 68 +++++++++++++ src/index.ts | 2 + tests/RadioGroup.test.tsx | 119 ++++++++++++++++++++++ 9 files changed, 489 insertions(+) create mode 100644 .changeset/radio-group-atom.md create mode 100644 src/components/RadioGroup.module.css create mode 100644 src/components/RadioGroup.stories.tsx create mode 100644 src/components/RadioGroup.tsx create mode 100644 tests/RadioGroup.test.tsx diff --git a/.changeset/radio-group-atom.md b/.changeset/radio-group-atom.md new file mode 100644 index 0000000..b48fa2f --- /dev/null +++ b/.changeset/radio-group-atom.md @@ -0,0 +1,13 @@ +--- +'@code-sherpas/pharos-react': minor +--- + +Add `RadioGroup` + `RadioGroupItem`, a set of mutually exclusive options (the +shadcn RadioGroup contract) over Base UI's `RadioGroup` + `Radio` — +`role="radiogroup"` / `role="radio"`, arrow-key navigation, single selection, +and the shared Button / Input / Checkbox / Switch focus ring. Completes the +boolean/choice form-control family (Checkbox → Switch → Radio). Compound +(group + item) because the selection semantics live on the group. Label-less by +design (D11): pair the group with a `