← All components

Primitive · 6 of 13

Radio

Two variants that differ far more than the name suggests: one is a small filled pill, the other a 44px-radius outlined card. Note the variant naming is inverted relative to Checkbox.

Vue island src/components/common/Radio.vue rendered live from compiled/Radio.js v-model

Props

PropTypeNotes
id *stringDoubles as the input's value and the emitted payload.
label *string12px, whitespace-nowrap.
name *stringGroups the inputs natively.
isCheckedbooleanBound to :checked.
modelValuestringDeclared, but the component emits id rather than reading this.
variant'primary' | 'secondary'primary = filled pill; secondary = outlined card.

Stories

Primary variant="primary" Filled pill on #0F0F15 (the site’s gray-1100), 8px radius. The budget selector in the consultation form.
Secondary variant="secondary" 2px border, 2.75rem radius, 30%-alpha ground. Mounted inside a .group wrapper here so hover works at all.

Findings

The secondary hover is dead code. Source styles it with group-hover:bg-surface-tertiary, which Tailwind only resolves when an ancestor carries the group class. The component renders no such ancestor and no caller adds one, so the hover never fires in the real app. The story above wraps each control in a .group equivalent purely so the intended behavior is visible — that wrapper is scaffolding, not source.

Same borrowed transition as Checkbox. Line 61 declares transition: var(--default-transition-duration) transform ease-in-out; against a custom property the project never defines — it resolves to 150ms only because Tailwind's default theme supplies it. Inside the site build (and here) the dot eases; outside one, it snaps.

modelValue is declared but never read. The component emits update:modelValue with its own id on change, and drives its visual state from the separate isChecked prop. So v-model alone will not check the button — the parent has to pass isChecked as well. Two props express one piece of state, and they can disagree.

Variant naming is inverted against Checkbox. Checkbox's secondary is the boxed-pill treatment and primary is bare. Radio's primary is the boxed pill and secondary is the larger card. Reaching for the same variant name across the two components gives opposite results.

The dot is 7px inside a 17px box. Unlike Checkbox the sizes are hard-coded pixels rather than token steps, and there is no size prop — so a radio cannot be matched to a small checkbox in the same row.