Skip to content
Home Theme Gallery

Radio group

The custom element is <hui-radio-group>.

Overview

A form-associated radio group. The host renders the items and marks them with role="radio"; the group owns the value, keeps exactly one item in the tab order and moves both focus and selection with the arrow keys.

Example

Radio group
Source
<hui-radio-group name="size" value="m" aria-label="Size">
<button class="hui-radio" role="radio" data-value="s">Small</button>
<button class="hui-radio" role="radio" data-value="m">Medium</button>
<button class="hui-radio" role="radio" data-value="l">Large</button>
</hui-radio-group>

The Go template that renders it:

<hui-radio-group name="size" value="{{ .Size }}" aria-label="Size">
{{ range .Options }}
<button class="hui-radio" role="radio" data-value="{{ .Value }}">{{ .Label }}</button>
{{ end }}
</hui-radio-group>

On a phone

A horizontal group wraps onto a second line when its options do not fit, rather than pushing the page sideways.

A horizontal group wraps 375 px wide
Source
<hui-radio-group name="plan" value="pro" orientation="horizontal" aria-label="Plan">
<button class="hui-radio" role="radio" data-value="free">Free</button>
<button class="hui-radio" role="radio" data-value="pro">Professional</button>
<button class="hui-radio" role="radio" data-value="team">Team</button>
<button class="hui-radio" role="radio" data-value="enterprise">Enterprise</button>
</hui-radio-group>

API

Attributes5
Attributes
NameTypeDefaultDescription
namestring''The form field name. With no name the group contributes no entry to FormData.
disabledbooleanfalseDisables selection and marks every item aria-disabled.
valuestring''The selected item value. Not reflected; the selected item is matched by its data-value, value attribute or trimmed text.
requiredbooleanfalseReflected; marks the group required for constraint validation.
orientation"vertical" | "horizontal""vertical"Read by the host stylesheet to lay the group out as a row. Declared as a class field but not a reactive property, so aria-orientation is set from the property and does not follow a later attribute change.
Properties5
Properties
NameTypeDefaultDescription
namestring''Reflects to the name attribute.
disabledbooleanfalseReflects to the disabled attribute.
valuestring''The selected value. Setting it re-syncs the item ARIA and tabindex.
requiredbooleanfalseReflects to the required attribute.
orientation"vertical" | "horizontal"'vertical'Drives aria-orientation. Not declared reactive.
Events3
Events
NameDescription
inputEmitted from the host when the selection changes, by click or by key. bubbles and composed; no detail.
changeEmitted from the host in the same turn as input. bubbles and composed; no detail.
clickThe slotted item click bubbles through the host; the group reads it to select the item.
Slots1
Slots
NameDescription
(default)The radio items. Each must match [role="radio"] and carry a data-value (or value, or a unique trimmed text).
CSS custom properties2
CSS custom properties
NameDefaultDescription
--hui-space-sm0.5remGap between the radio items.
--hui-focus-ring-width3pxWidth of the keyboard focus ring.
::part() hooks1
::part() hooks
NameDescription
groupThe wrapper that carries role="radiogroup". It lays items out with --hui-space-sm between them.

States

Horizontal

orientation="horizontal" lays the options in a row and moves the selection with Left and Right. The row wraps when it runs out of width rather than pushing the page.

Horizontal
Source
<hui-radio-group name="size" value="m" orientation="horizontal" aria-label="Size">
<button class="hui-radio" role="radio" data-value="s">S</button>
<button class="hui-radio" role="radio" data-value="m">M</button>
<button class="hui-radio" role="radio" data-value="l">L</button>
</hui-radio-group>

Disabled

disabled on the group disables every option in it, and the group keeps its name and its value.

Disabled
Source
<hui-radio-group name="size" value="m" disabled aria-label="Size">
<button class="hui-radio" role="radio" data-value="s">Small</button>
<button class="hui-radio" role="radio" data-value="m">Medium</button>
</hui-radio-group>

Server-side mechanics

The server renders the item markup, each a [role="radio"] carrying data-value (or value), and owns the group's name, value, required and disabled. The group owns the selection and submits the chosen item value under name through ElementInternals; with no name it submits nothing. Because value is not reflected and the items are light-DOM children, an hx-swap must re-render both the group's value attribute and the items it selects among, and a swap mid-selection discards the choice. Server-rendered validation is required plus aria-invalid="true" or a hui-field invalid wrapper.

Accessibility

  • The wrapper is role="radiogroup" with aria-orientation; each slotted item is expected to carry role="radio".
  • The group sets aria-checked and the roving tabindex on the items itself, so the item markup stays static.
  • A disabled group keeps the items focusable but marks them aria-disabled and refuses selection.
  • Item values are read from data-value first, then the value attribute, then the trimmed text content.

Keyboard

Keyboard
KeysAction
TabEnters and leaves the group as a single tab stop; the selected item, or the first, holds tabindex="0".
Arrow keysUp, Down, Left and Right all move and select, wrapping at the ends.
Space, EnterSelects the focused item.
Home, EndJump to the first and last item.

Gotchas

  • orientation is a class field but is not in static properties, so setting the attribute lays the group out horizontally without updating aria-orientation; the host stylesheet still reads :host([orientation='horizontal']).
  • value is not reflected, so the selected item must be re-seeded from the attribute on every render.