Pho Design System

Radio Group

Chooses exactly one option from a small, visible set. Built on Base UI RadioGroup + Radio, with arrow-key navigation between options. When the set is long or space is tight, prefer Select.

Import

import {
  RadioGroup,
  Radio,
  RadioCard,
} from "@photon-ai/pho-ui/components/radio-group";

Basic

Each Radio needs a value; label makes the whole row selectable. Leave the group uncontrolled with defaultValue.

<RadioGroup defaultValue="standard">
  <Radio value="standard" label="Standard — 3–5 business days" />
  <Radio value="express" label="Express — next business day" />
  <Radio value="overnight" label="Overnight" />
</RadioGroup>

Disabled

Set disabled on a single Radio to remove just that option, or on RadioGroup to disable every option at once. Disabled options are skipped by arrow-key navigation.

<Radio value="overnight" label="Overnight — not available" disabled />

Controlled

Drive the selection yourself with value and onValueChange when the choice feeds other state.

Selected: standard

const [value, setValue] = useState("standard");

<RadioGroup value={value} onValueChange={setValue}>
  <Radio value="standard" label="Standard" />
  <Radio value="express" label="Express" />
  <Radio value="overnight" label="Overnight" />
</RadioGroup>;

Card

RadioCard wraps the same Radio in a bordered, selectable tile — pass title and an optional description instead of label. Use it when each option carries enough weight (a plan, a brand type) to deserve more visual presence than a plain labeled dot. The whole tile is the hit target and carries the press feedback; arrange a set of them with RadioGroup as usual.

OrganizationA registered company or non-profit.Sole proprietorAn individual with lighter carrier vetting.
<RadioGroup defaultValue="org" className="flex flex-col gap-2 sm:flex-row">
  <RadioCard
    value="org"
    title="Organization"
    description="A registered company or non-profit."
  />
  <RadioCard
    value="sole"
    title="Sole proprietor"
    description="An individual with lighter carrier vetting."
  />
</RadioGroup>

Card with icon

Pass icon when the option has a glyph — it stacks above the title and replaces the radio dot.

OrganizationA registered company or non-profit.Sole proprietorAn individual with lighter carrier vetting.
<RadioGroup defaultValue="org" className="flex flex-col gap-2 sm:flex-row">
  <RadioCard
    value="org"
    title="Organization"
    description="A registered company or non-profit."
    icon={<IconBuilding />}
  />
  <RadioCard
    value="sole"
    title="Sole proprietor"
    description="An individual with lighter carrier vetting."
    icon={<IconUser />}
  />
</RadioGroup>

Anatomy

Props

RadioGroup

Radio

RadioCard

Accessibility

RadioGroup renders role="radiogroup" and each Radio renders role="radio" with aria-checked. Passing label wraps the dot and text in a <label>, so the whole row is both clickable and the option’s accessible name; without a label, give the Radio an aria-label. required and readOnly surface as aria-required and aria-readonly on the group. RadioCard renders its title and description inside the radio tile, so they compose into the option’s accessible name.

The group is a single tab stop: roving tabindex keeps only one radio tabbable, so Tab moves past the group rather than through each option.

Best practices