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
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.
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
| Name | Type | Default | Description |
|---|---|---|---|
name | string | '' | The form field name. With no name the group contributes no entry to FormData. |
disabled | boolean | false | Disables selection and marks every item aria-disabled. |
value | string | '' | The selected item value. Not reflected; the selected item is matched by its data-value, value attribute or trimmed text. |
required | boolean | false | Reflected; 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
| Name | Type | Default | Description |
|---|---|---|---|
name | string | '' | Reflects to the name attribute. |
disabled | boolean | false | Reflects to the disabled attribute. |
value | string | '' | The selected value. Setting it re-syncs the item ARIA and tabindex. |
required | boolean | false | Reflects to the required attribute. |
orientation | "vertical" | "horizontal" | 'vertical' | Drives aria-orientation. Not declared reactive. |
Events3
| Name | Description |
|---|---|
input | Emitted from the host when the selection changes, by click or by key. bubbles and composed; no detail. |
change | Emitted from the host in the same turn as input. bubbles and composed; no detail. |
click | The slotted item click bubbles through the host; the group reads it to select the item. |
Slots1
| Name | Description |
|---|---|
(default) | The radio items. Each must match [role="radio"] and carry a data-value (or value, or a unique trimmed text). |
CSS custom properties2
| Name | Default | Description |
|---|---|---|
--hui-space-sm | 0.5rem | Gap between the radio items. |
--hui-focus-ring-width | 3px | Width of the keyboard focus ring. |
::part() hooks1
| Name | Description |
|---|---|
group | The 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.
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.
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"witharia-orientation; each slotted item is expected to carryrole="radio". - The group sets
aria-checkedand the rovingtabindexon the items itself, so the item markup stays static. - A disabled group keeps the items focusable but marks them
aria-disabledand refuses selection. - Item values are read from
data-valuefirst, then thevalueattribute, then the trimmed text content.
Keyboard
| Keys | Action |
|---|---|
Tab | Enters and leaves the group as a single tab stop; the selected item, or the first, holds tabindex="0". |
Arrow keys | Up, Down, Left and Right all move and select, wrapping at the ends. |
Space, Enter | Selects the focused item. |
Home, End | Jump to the first and last item. |
Gotchas
orientationis a class field but is not instatic properties, so setting the attribute lays the group out horizontally without updatingaria-orientation; the host stylesheet still reads:host([orientation='horizontal']).valueis not reflected, so the selected item must be re-seeded from the attribute on every render.