VisorVisor
ComponentsForm

Radio Group

A set of mutually exclusive radio options. Built on Radix UI.

Options

Disabled

Card variant

variant="card" draws each item as a card with a leading icon, a title and a one-line description. The chosen card is a lifted fill with a light inset edge and a check mark, so selection is never carried by colour alone. A disabled item stays in place and stays readable, is skipped by the arrow keys, and says why when you pass aria-describedby.

Public needs a verified booking.

Item propPurpose
iconLeading icon or swatch; decorative and hidden from assistive tech
titleThe option's name; it is the item's accessible name
descriptionOne line saying what the option means; it describes the item
aria-describedbyAn id saying why an item is disabled; announced after the description

The resting edge reads the shared --control-edge-* tokens, so --control-edge-width: 0 turns it off; focus and invalid keep drawing at --control-state-edge-width. Per-component overrides: --radio-card-bg, --radio-card-selected-bg, --radio-card-selected-border, --radio-card-border, --radio-card-radius, --radio-card-icon-size.

Installation

npx visor add radio-group

This copies two files into your project:

  • components/ui/radio-group/radio-group.tsx — the component
  • components/ui/radio-group/radio-group.module.css — the styles

Usage

import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group/radio-group';
import { Label } from '@/components/ui/label/label';

<RadioGroup defaultValue="a">
  <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
    <RadioGroupItem value="a" id="a" />
    <Label htmlFor="a">Option A</Label>
  </div>
  <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
    <RadioGroupItem value="b" id="b" />
    <Label htmlFor="b">Option B</Label>
  </div>
</RadioGroup>

API Reference

RadioGroupProps

PropTypeDefaultDescription
valuestring—Controlled value of the selected radio item.
defaultValuestring—Default selected value for uncontrolled usage.
onValueChange(value: string) => void—Callback when the selected value changes.
disabledbooleanfalseDisables all radio items in the group.

Sub-components

ComponentElementPurpose
RadioGroup<div>Root container with role="radiogroup" managing selection state
RadioGroupItem<button>Individual radio option with role="radio" and circle indicator (or, in the card variant, icon, title, description and check mark)

Accessibility

  • Built on @radix-ui/react-radio-group with full WAI-ARIA radio group semantics
  • RadioGroup renders with role="radiogroup"; each RadioGroupItem renders with role="radio"
  • Wrap the group in a <Fieldset> with a <FieldsetLegend> to give the group an accessible name
  • Each item should have an associated <Label> via matching htmlFor/id
  • Keyboard: Arrow keys move selection within the group and skip disabled items; Tab moves focus to/from the group
  • Card variant: the title names the item (aria-labelledby), the description and any aria-describedby id describe it, and the chosen card carries a check mark as well as a fill