components / radiogroup

RadioGroup

Data-driven single-selection group with native radio semantics and predictable grouped layout.

Overview

What it is

This section explains the intent of the component before the implementation details.

  • RadioGroup handles single selection when all options should remain visible at once.
  • It keeps both option descriptions and group labeling within one consistent contract.

Aliases

radio buttons

Decision guide

How to choose it

Use these notes to decide quickly whether this is the right DK component for the job.

Decision guide

  • Choose RadioGroup when the interaction should feel deliberate, documented, and reusable across product surfaces.
  • If the scenario is more specialized than the current RadioGroup contract, prefer a simpler component or build a dedicated workflow on top of the lower-level DK primitives.

Do

  • Keep the RadioGroup label, value, or status language direct enough to scan quickly.
  • Use the documented size and state recipes instead of inventing one-off variants in product code.

Do not

  • Do not hide the main purpose of RadioGroup behind decorative copy or overloaded surface treatments.
  • Do not stretch RadioGroup into a workflow it was not designed to handle just because the visuals are close.

Usage

When to use

Prefer these situations when choosing this component.

  • Use RadioGroup when the number of options is small enough to display together.
  • Use descriptions when the difference between options needs a short explanation.

Usage

When not to use

These patterns are better served by a different component or a simpler surface.

  • Do not use RadioGroup for many options; use Select or Combobox.
  • Do not use RadioGroup for rapid segmented view switching; use SegmentedControl.

Anti-patterns

  • Using RadioGroup as a generic layout container instead of a purpose-built interaction or content surface.
  • Forking the documented recipe in product code instead of extending the shared DK contract.

Migration notes

  • Migrate legacy radiogroup usage by mapping existing variants and states onto the documented DK props before porting any custom styling.
  • Treat the docs examples as the reference contract for accessibility and event behavior during adoption.

Anatomy

Structural parts

The anatomy explains which pieces matter to the recipe and accessibility model.

radio group

group

Wraps the full set of mutually exclusive choices.

single radio option

item

Each selectable option with label and optional description.

API

Public interface

The docs contract distinguishes props, DOM events, and slots so integration behavior is explicit.

Props

NameTypeDefaultDescription
valuestringControlled selected value.
itemsArray<{ value: string; label: string; description?: string; disabled?: boolean }>Available radio options.
orientation'vertical' | 'horizontal''vertical'Chooses how the options are laid out.
labelstringVisible group label.
descriptionstringSupporting helper copy for the full group.
errorstringProblem copy for validation or blocked flows.
requiredbooleanfalseMarks the group as required.
disabledbooleanfalseDisables the whole group.
namestringNative radio group name.
size'sm' | 'md''md'Chooses the size recipe.
onChange(detail: { value: string | undefined }) => voidCallback fired when selection changes.
themeThemeContractOverrides the compiled DK theme used to resolve tokens and recipes for this component.

Events

NamePayloadDescription
change{ value: string | undefined }Fires when the component commits a new value.

Slots

NameDescription
defaultRadioGroup is data-driven in v1 and does not expose option slots.

Recipes

Variants, sizes, and states

These notes summarize the intended recipe surface rather than exposing raw implementation detail first.

Variants

  • vertical: Stacked options with more room for descriptions.
  • horizontal: Compact single-line grouping for shorter labels.

Sizes

  • sm: Compact group
  • md: Default group size

States

  • unselected: No option chosen
  • selected: One option chosen
  • disabled: Whole group or item disabled

Accessibility

Accessibility contract

This is the behavior the component promises to assistive tech and keyboard users today.

Semantics

  • Uses native radio inputs inside a labeled radio group.

Keyboard

  • Arrow keys move selection within the group according to native radio behavior.

Screen readers

  • Group label and option descriptions remain associated with the radios.

Checklist

  • Verify the visible label or title still produces a clear accessible name.
  • Verify focus remains obvious in every supported state and size.
  • Verify disabled, selected, and error states do not rely on color alone.

Implementation

Tokens and slot vars

This section shows the representative compiled slot variables that the runtime consumes for the selected design system.

root

--dk-radio-group-gap
clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)
--dk-radio-group-item-gap
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)
--dk-radio-group-hit-size
44px

legend

--dk-radio-legend-color
#16181c
--dk-radio-legend-size
clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)
--dk-radio-legend-weight
600

item

--dk-radio-item-gap
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)

control

--dk-radio-control-bg
#f2f5fb
--dk-radio-control-border
#95989d
--dk-radio-control-size
20px
--dk-radio-control-border-width
1.5px
--dk-radio-focus-ring-color
#2072e4

mark

--dk-radio-mark-bg
#2072e4
--dk-radio-mark-size
10px

itemLabel

--dk-radio-item-label-color
#16181c
--dk-radio-item-label-size
clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)
--dk-radio-item-label-weight
600

itemDescription

--dk-radio-item-description-color
#16181c
--dk-radio-item-description-size
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)

description

--dk-radio-description-color
#16181c
--dk-radio-description-size
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)

error

--dk-radio-error-color
#cc272e
--dk-radio-error-size
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)

Implementation notes

  • RadioGroup stays data-driven in v1 so option metadata remains easy to inspect and test.

Implementation checklist

  • Use the public component API first and only drop to lower-level primitives when the component contract genuinely does not fit.
  • Keep theme overrides token-driven so proofs and recipes stay meaningful.
  • Verify the shipped examples and proof fixtures still represent the real product scenario you are implementing.

Examples

Copy-paste examples

Each example is intentionally practical, grouped by starter, common pattern, and edge-case coverage.

Examples
3
Depth
baseline

Starter

1 example

starter

Starter: cadence selection

A small mutually exclusive option set.

Copy snippet

<RadioGroup label="Digest cadence" items={cadenceItems} value="daily" />

Common patterns

1 example

common

Common: compact horizontal set

Horizontal orientation fits short labels.

Copy snippet

<RadioGroup orientation="horizontal" items={sizeItems} value="md" />

Edge cases

1 example

edge

Edge: explained options

Descriptions help when the options are subtle.

Copy snippet

<RadioGroup label="Review cadence" items={[{ value: 'daily', label: 'Daily', description: 'Fast status rhythm.' }, { value: 'weekly', label: 'Weekly', description: 'Lower-noise update cadence.' }]} />

Verification

Curated proof fixtures

Proofs stay visible in the docs so the system shows what it can guarantee, not just what it can render.

default (orientation=vertical|size=sm)

pass

orientation=vertical|size=sm

  • Contrast: 65.1 Lc
  • Target: 44px
  • Rows: 44px

horizontal (orientation=horizontal|size=sm)

pass

orientation=horizontal|size=sm

  • Contrast: 65.1 Lc
  • Target: 44px
  • Rows: 44px

default (orientation=vertical|size=md)

pass

orientation=vertical|size=md

  • Contrast: 65.1 Lc
  • Target: 44px
  • Rows: 44px

horizontal (orientation=horizontal|size=md)

pass

orientation=horizontal|size=md

  • Contrast: 65.1 Lc
  • Target: 44px
  • Rows: 44px

default (orientation=vertical|size=lg)

pass

orientation=vertical|size=lg

  • Contrast: 65.1 Lc
  • Target: 44px
  • Rows: 44px

horizontal (orientation=horizontal|size=lg)

pass

orientation=horizontal|size=lg

  • Contrast: 65.1 Lc
  • Target: 44px
  • Rows: 44px

selected (orientation=vertical|size=sm)

pass

orientation=vertical|size=sm

  • Contrast: 65.1 Lc
  • Target: 44px
  • Rows: 44px

selected (orientation=horizontal|size=sm)

pass

orientation=horizontal|size=sm

  • Contrast: 65.1 Lc
  • Target: 44px
  • Rows: 44px

selected (orientation=vertical|size=md)

pass

orientation=vertical|size=md

  • Contrast: 65.1 Lc
  • Target: 44px
  • Rows: 44px

selected (orientation=horizontal|size=md)

pass

orientation=horizontal|size=md

  • Contrast: 65.1 Lc
  • Target: 44px
  • Rows: 44px

selected (orientation=vertical|size=lg)

pass

orientation=vertical|size=lg

  • Contrast: 65.1 Lc
  • Target: 44px
  • Rows: 44px

selected (orientation=horizontal|size=lg)

pass

orientation=horizontal|size=lg

  • Contrast: 65.1 Lc
  • Target: 44px
  • Rows: 44px