<bmx-radio-group>
One answer from several. The group holds the value, the keyboard, the tab
order, the form participation and the validity; bmx-radio renders an option
and nothing more. That division is what makes two options selected at once
impossible rather than merely unlikely.
15 properties · 2 events · 4 methods · 4 parts
Example
Show markup
<div class="row">
<bmx-radio-group label="Contact me by" value="email" description="Arrow keys move and choose in one keystroke, as they do in a native radio group.">
<bmx-radio value="email" label="Email"></bmx-radio>
<bmx-radio value="post" label="Post"></bmx-radio>
<bmx-radio value="phone" label="Telephone"></bmx-radio>
</bmx-radio-group>
</div>
<div class="row">
<bmx-radio-group label="Delivery" orientation="horizontal" required="true">
<bmx-radio value="standard" label="Standard"></bmx-radio>
<bmx-radio value="express" label="Express"></bmx-radio>
<bmx-radio value="collect" label="Click and collect" disabled="true"></bmx-radio>
</bmx-radio-group>
</div>
THE KEYBOARD IS NOT A TOOLBAR'S
A radio group and a button group both take one Tab stop and move on the arrows, and there the resemblance ends:
- Arrow keys select. Moving to an option chooses it, in one keystroke. There is no way to look at an option without selecting it, which is how every native radio group on every platform behaves and what a user who presses Down expects.
- Both axes always work. Up and Left move back, Down and Right move forward, whether the group is a column or a row. A radio group is a list of answers rather than a strip of controls.
- Tabbing in lands on the answer, not on wherever focus happened to be left. Landing anywhere else invites the user to press an arrow key and change a value they only meant to look at.
The arithmetic for all three is in src/core/choice.ts, unit tested. The
component's job is to read the options, hand them their tabindex, and move
focus - which is the part that needs a browser.
WHY THE ROLE IS ON A DIV INSIDE THE SHADOW ROOT
role="radiogroup" needs an accessible name, and the group's caption lives
in this component's shadow root. An aria-labelledby on the host could not
see it: IDREF attributes do not cross a shadow boundary, so the reference
would silently resolve to nothing and the group would be announced as an
unnamed group - which is the failure mode that looks completely fine.
Putting the role on a <div> beside the caption puts both in the same root,
so the reference resolves. The slotted options are still inside it where the
accessibility tree is concerned, because that tree is built from the
flattened tree and the <slot> is in the div.
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
description |
description |
string |
— | Help text below the options. |
disabled |
disabled |
boolean |
false |
Disable every option in the group. Options may still be disabled individually. |
errorText |
error-text |
string |
— | An error supplied by the consumer - a server response, typically. |
fullWidth |
full-width |
boolean |
false |
Stretch to the width of the container. |
hideLabel |
hide-label |
boolean |
false |
Hide the caption visually while keeping it as the accessible name. |
label |
label |
string |
— | The group's caption - the question the options answer. Not optional in practice: a radiogroup announced with no name tells a screen reader user only that some options have been grouped, and the options themselves rarely make sense without the question. |
messages |
property only | BmxFieldMessages |
— | Replacements for the default wording, by reason. Accepts the JSON spelling of the object as well, for templates that can only write attributes. See src/core/markup.ts. |
name |
name |
string |
— | The field's name in the form it belongs to. |
orientation |
orientation |
BmxOrientation |
'vertical' |
Layout direction. Both axes move the selection either way; this is visual. |
required |
required |
boolean |
false |
Require an answer before the form will submit. |
size |
size |
BmxSize |
'md' |
Applied to every option that has not set its own. |
tone |
tone |
BmxTone |
'primary' |
Applied to every option that has not set its own. |
validateOn |
validate-on |
BmxValidateOn |
'blur' |
When the group is willing to reveal a problem. |
value |
value |
string | null |
null |
The chosen option's value. Two-way: the group writes back to it. |
wrapFocus |
wrap-focus |
boolean |
true |
Whether arrowing past the last option returns to the first. |
Events
| Event | Detail | Description |
|---|---|---|
bmxChange |
BmxRadioGroupChangeDetail |
Fired when the chosen option changes. |
bmxValidityChange |
BmxRadioGroupValidityDetail |
Fired whenever the resolved validity changes. |
Methods
| Method | Signature | Description |
|---|---|---|
checkValidity |
checkValidity() => Promise<boolean> |
Validate now and return whether the group passed, without revealing anything. |
refresh |
refresh() => Promise<void> |
Re-read the options. Call after adding or removing them imperatively. |
reportValidity |
reportValidity() => Promise<boolean> |
Validate, reveal any problem, and focus the group if it has one. |
setFocus |
setFocus() => Promise<void> |
Move focus to the group - onto the chosen option, or the first available one. |
Slots
| Slot | Description |
|---|---|
(default) |
bmx-radio elements. |
description |
Rich help text, in place of the description property. |
label |
Rich caption content, in place of the label property. |
CSS shadow parts
| Part | Description |
|---|---|
base |
The element carrying role="radiogroup". |
description |
The help text. |
error |
The error message. |
label |
The group's caption. |
CSS custom properties
| Property | Description |
|---|---|
--bmx-radio-group-gap |
Space between the options. |
--bmx-radio-group-label-font-size |
The question's font size. |
--bmx-radio-group-stack-gap |
Space between the question, the options and the supporting text. |
--bmx-radio-group-support-font-size |
Font size of the description and error message. |