Skip to content

HabRadio ​

One of two to five mutually exclusive options, all visible at once. More than that is a HabSelect.

Two to five visible options. More than that is a select.
vue
<HabRadio
	v-model="plan"
	name="plan"
	value="pro"
	label="Pro"
	help="Everything in Free, plus support."
/>

Bind v-model on every radio in the group and give each its own value; the radio is checked when the model equals its value, and choosing it sets the model.

Give every radio in one group the same name. That is what makes the browser treat them as a single control: arrow keys move between them, only one submits, and a screen reader announces "2 of 3".

The dot is the border ​

A checked radio is a 5px border on a 20px circle, which leaves a 10px well of surface in the middle. There is no separate dot element — (20 − 1) / 2 is where the 5 comes from.

Read-only ​

Read-only: the choice at full contrast, and choosing another changes nothing

readonly shows the choice to be read: the checked dot and the empty circles in full contrast, the label in full ink. Clicking a radio or its label, Space and the arrow keys change nothing, and update:modelValue is never emitted. The radio stays focusable, unlike disabled, which says "not now" and is dimmed and outranks it. See HabReadonly for the difference, and for making a whole region read-only at once.

vue
<fieldset role="radiogroup" aria-readonly="true">
	<legend>Plan</legend>
	<HabRadio v-model="plan" name="plan" value="free" label="Free" readonly />
	<HabRadio v-model="plan" name="plan" value="pro" label="Pro" readonly />
</fieldset>

The group says it is read-only, not the radio. ARIA allows aria-readonly on a radiogroup and not on a radio, so unlike HabCheckbox and HabSwitch the input carries no aria-readonly. The grouping element is yours: give it role="radiogroup" and aria-readonly="true" while the radios are read-only.

Accessibility ​

  • A real <input type="radio"> stays in the DOM, only visually hidden, so the group behaves natively: arrow-key navigation, form submission and roving focus are the platform's.
  • The mark is the input's immediately following sibling, which is where base.css draws the focus indicator — the input is 0×0 and the native one has nothing to draw on.
  • The label names the control, the help describes it.
  • Wrap the group in a <fieldset> with a <legend>, so the question is announced along with the options. Habitus renders one radio; the grouping is yours.
  • Give it a label, or an aria-label. Habitus warns when it has neither.

API ​

Props ​

PropTypeDefaultDescription
disabledbooleanfalse—
helpstringundefinedQuiet second line — what the option actually does.
labelstringundefinedThe option's name.
modelValuestring | numberundefinedThe group's selected value, for v-model. The radio is checked when this equals its own value.
namestringundefinedShared name for the group. Give every radio in one group the same name so the browser treats them as one control — arrow keys move between them, and only one submits.
readonlybooleanundefinedThe choice is shown to be read, not changed: checked or unchecked in full contrast and the label in full ink. Clicking the radio or its label, Space and the arrow keys change nothing, and update:modelValue is never emitted. Still focusable — unlike disabled, which says "not now", is dimmed, and outranks it. ARIA puts aria-readonly on the group, not the radio, so set it on your role="radiogroup" element. Unset, it follows a surrounding HabReadonly; false keeps this radio editable inside one.
value*string | number—This option's value. What the group's model becomes when it is chosen.

* required

Events ​

EventPayloadDescription
update:modelValuevalue: string | number—

Proprietary — internal use only.