Zetta v1.6

Components · Forms & selection

Radio

Mutually exclusive selection control. Split from zetta-form-v1_4.md (removed in v1.5) in v1.4.

Preview

Build with the skill

No package to install — hand this to your AI to generate or migrate the component in your stack.

Prompt for your AI
Apply the Zetta "Radio" component to this project using the zetta-design-md skill.

WHEN TO USE
- Migrating or fixing existing code (the common case): audit the current implementation against the `radio` spec and CORRECT every deviation — replace off-spec values, don't only add new ones.
- New component: build it to the `radio` spec in this project's stack.

AUDIT & FIX (when the component already exists)
Compare it to the skill's spec and fix, don't just append: hardcoded colors/hex, off-scale radius, wrong fonts, dark/navy fills, shadows used in place of borders (or missing borders), brand lime used as a CTA or interaction (must be primary indigo), and any missing or wrong hover / focus / active / disabled / invalid state or missing 2px focus ring. Replace each with the correct Zetta token.

ALWAYS
- Pull the authoritative spec and tokens from the skill — never guess or hardcode values.
- Use this project's own component conventions and framework idioms.
- Honor the Zetta guardrails (brand = identity only, primary = interaction, shadows for overlays) and verify in Light, Dark, and Accessibility.

Anatomy & rules

◉  Option A   ← selected: {colors.primary} border + 10px indicator dot
○  Option B   ← unselected: {colors.border-strong} border
○  Option C
State Border Indicator
Default (unselected) {colors.border-strong} None
Hover {colors.border-strong} None
Focus {colors.border-strong} + 2px solid {colors.border-focus} outline None
Selected {colors.primary} {colors.primary} 10px dot
Disabled (unselected) {colors.disabled-border} None
Disabled (selected) {colors.disabled-border} {colors.disabled-text} dot

When to use

  • Selecting one option from a small set (2–6 options).
  • When all options should be visible simultaneously for comparison.

When not to use

Do

  • Always group related radios under a <fieldset> + <legend>.
  • Pre-select the most common or safest option as the default.
  • List options in a logical order: most common first, or alphabetical.

Don't

  • Don't use radio for actions — use buttons.
  • Don't allow a radio group with no option selected after initial render (always have a default).
  • Option labels: noun or noun phrase. Sentence case. No trailing punctuation.
  • Group legend: noun describing what's being chosen. "Billing cycle", "Notification frequency".
  • Keep option labels parallel in structure: all nouns, or all adjectives — don't mix.

Keyboard

Key Behavior
Tab Move focus into the radio group
Arrow Up / Left Select previous option
Arrow Down / Right Select next option
Space Select focused option (if not already selected)

Accessibility

  • ARIA role: implicit role="radio" on <input type="radio">.
  • Group all options in <fieldset> + <legend> — the legend is announced before each option.
  • name attribute groups radios — required for mutually exclusive behavior.
  • aria-disabled="true" on disabled radios (in addition to disabled attribute).
  • Hit area: 12px padding expands touch target toward 44×44px minimum.

Spec

Values and token references resolved straight from the Zetta spec.

radio
backgroundColor
transparent
borderColor
{colors.border-strong}
borderWidth
2px
borderRadius
{rounded.full}
size
20px
cursor
pointer
padding
12px
radio-hover
borderColor
{colors.border-strong}
radio-focus
outline
2px solid {colors.border-focus}
outlineOffset
2px
radio-checked
borderColor
{colors.primary}
indicatorColor
{colors.primary}
indicatorSize
10px
radio-disabled
borderColor
{colors.disabled-border}
cursor
not-allowed
radio-checked-disabled
borderColor
{colors.disabled-border}
indicatorColor
{colors.disabled-text}