Radio Group
UI pattern
A radio group lets users choose one of a set of options by checking one of its radio buttons. Checking a radio button unchecks others, so no more than one is checked at a time. A group can start with none checked, which asks users to make a choice rather than accept a default.
The group and its radio buttons work as a question and a set of mutually exclusive answers. The group has a name that states the question, such as "Preferred contact method", and each radio button has a name that states one answer.
A radio group differs from a set of checkboxes, which lets users check any number of options. Related Patterns lists other patterns that offer a choice, including some suited to longer lists than a group can show at once.
There are two ways to build one. The native construction is a set of <input type="radio"> elements that share a name attribute, grouped inside a <fieldset> element whose <legend> element names the group. Each input carries a value, which its associated <label> element identifies.
In the native construction, the browser supplies the roles, the checked state, keyboard operation, and form submission without ARIA or code. Build this one whenever you can. When the design calls for a different appearance, style the native inputs rather than replacing them.
The ARIA construction is a container with the radiogroup role holding elements with the radio role, each carrying aria-checked. Script handles behavior: checking a radio button on a click or a key action, moving focus with the arrow keys, keeping the group to one tab stop, and recording or submitting its value with a form.
When to Use
Use a radio group when:
- Users choose exactly one option from a small set of options that exclude each other.
- Users benefit from seeing every option at once, to compare them before choosing.
- A choice is required and no option makes a fair default. Start the group with no radio button checked.
Don't use a radio group when:
- Users can choose more than one option. Use a checkbox for each option.
- There's a single option. Use a checkbox to choose the option or a switch to turn a feature or setting on or off.
- The list of options is long, changes often, or has to fit in a small space. Use a listbox or a combobox.
- Each option runs a command rather than recording a choice. Use a button for each command.
- Selection controls which content panels to display. Use a tablist.
Examples
Code in this section is meant to demonstrate techniques rather than requirements and lacks features needed in a production application.
Each example builds the same group of three options for a preferred contact method, with Email checked.
Native HTML Radio Group
The radio buttons share name="contact", which makes them one group: checking one unchecks the others, and the browser gives the group a single tab stop and arrow-key navigation. The <fieldset> element groups them for assistive technology, and its <legend> names the group. Wrapping each input in a <label> names the radio button and makes the label text a click target too.
Native radio group HTML
<fieldset>
<legend>Preferred contact method</legend>
<label>
<input
type="radio"
name="contact"
value="email"
checked>
Email
</label>
<label>
<input
type="radio"
name="contact"
value="phone">
Phone
</label>
<label>
<input
type="radio"
name="contact"
value="mail">
Mail
</label>
</fieldset>ARIA Radio Group With Roving Tabindex
The container carries the radiogroup role and takes its name from the visible text its aria-labelledby references. Each radio button is a <div> with the radio role and aria-checked. The checked radio button holds tabindex="0" and the others tabindex="-1", so the group is one stop in the tab order and the stop lands on the checked option.
ARIA radio group with roving tabindex HTML
<span id="contact-label">Preferred contact method</span>
<div
role="radiogroup"
aria-labelledby="contact-label">
<div
role="radio"
aria-checked="true"
tabindex="0">
Email
</div>
<div
role="radio"
aria-checked="false"
tabindex="-1">
Phone
</div>
<div
role="radio"
aria-checked="false"
tabindex="-1">
Mail
</div>
</div>CSS draws each radio button's circle from its aria-checked value, so the appearance can't drift from the state users are told about. The marks are drawn with borders, which browsers render regardless of a user's high-contrast or forced-color settings.
ARIA radio group with roving tabindex CSS
[role="radio"] {
position: relative;
padding-inline-start: 1.5em;
cursor: pointer;
}
[role="radio"]::after,
[role="radio"]::before {
content: "";
position: absolute;
box-sizing: border-box;
border-radius: 50%;
}
[role="radio"]::before {
inset-inline-start: 0;
inset-block-start: 0.3em;
inline-size: 1em;
block-size: 1em;
border: 0.1em solid currentColor;
}
[role="radio"]::after {
inset-inline-start: 0.25em;
inset-block-start: 0.55em;
inline-size: 0;
block-size: 0;
border: 0.25em solid currentColor;
display: none;
}
[role="radio"][aria-checked="true"]::after {
display: block;
}A click, Space, and the arrow keys all check a radio button through one function, which moves the tab stop along with the checked state. The arrow keys wrap from the last radio button to the first and from the first to the last.
ARIA radio group with roving tabindex JavaScript
class RadioGroup {
#radios
constructor(group) {
this.#radios = [...group.querySelectorAll('[role="radio"]')]
this.#radios.forEach((radio) => {
radio.addEventListener('click', () => this.check(radio))
})
group.addEventListener('keydown', (event) => this.#onKeydown(event))
}
check(radio) {
this.#radios.forEach((r) => {
const checked = r === radio
r.setAttribute('aria-checked', String(checked))
r.tabIndex = checked ? 0 : -1
})
radio.focus()
}
#onKeydown(event) {
const current = this.#radios.indexOf(document.activeElement)
if (current < 0) return
const last = this.#radios.length - 1
let next
switch (event.key) {
case ' ':
next = current
break
case 'ArrowRight':
case 'ArrowDown':
next = current === last ? 0 : current + 1
break
case 'ArrowLeft':
case 'ArrowUp':
next = current === 0 ? last : current - 1
break
default:
return
}
// keep Space and the arrow keys from scrolling the page
event.preventDefault()
this.check(this.#radios[next])
}
}
document.querySelectorAll('[role="radiogroup"]')
.forEach((group) => new RadioGroup(group))ARIA Radio Group With aria-activedescendant
Here the group itself holds tabindex="0" and keeps keyboard focus, and its aria-activedescendant names the checked radio button by its id. The radio buttons carry no tabindex, and a click on one focuses the group around it.
ARIA radio group with aria-activedescendant HTML
<span id="contact-label">Preferred contact method</span>
<div
role="radiogroup"
aria-labelledby="contact-label"
tabindex="0"
aria-activedescendant="contact-email">
<div
role="radio"
id="contact-email"
aria-checked="true">
Email
</div>
<div
role="radio"
id="contact-phone"
aria-checked="false">
Phone
</div>
<div
role="radio"
id="contact-mail"
aria-checked="false">
Mail
</div>
</div>The radio buttons draw their circles with the same CSS as the previous example. Focus stays on the group, so the browser draws no focus indicator on the radio buttons, and CSS marks the active one while the group has focus.
ARIA radio group with aria-activedescendant CSS
[role="radiogroup"]:focus-visible [aria-checked="true"] {
outline: 2px solid currentColor;
outline-offset: 2px;
}The script checks a radio button and points aria-activedescendant at it in the same step, so the checked state and the radio button assistive technology reports as focused can't disagree.
ARIA radio group with aria-activedescendant JavaScript
class ActiveDescendantRadioGroup {
#group
#radios
constructor(group) {
this.#group = group
this.#radios = [...group.querySelectorAll('[role="radio"]')]
this.#radios.forEach((radio) => {
radio.addEventListener('click', () => this.check(radio))
})
group.addEventListener('keydown', (event) => this.#onKeydown(event))
}
check(radio) {
this.#radios.forEach((r) => {
r.setAttribute('aria-checked', String(r === radio))
})
this.#group.setAttribute('aria-activedescendant', radio.id)
}
#onKeydown(event) {
const active = document.getElementById(
this.#group.getAttribute('aria-activedescendant')
)
// with nothing checked yet, start from the first radio button
const current = Math.max(0, this.#radios.indexOf(active))
const last = this.#radios.length - 1
let next
switch (event.key) {
case ' ':
next = current
break
case 'ArrowRight':
case 'ArrowDown':
next = current === last ? 0 : current + 1
break
case 'ArrowLeft':
case 'ArrowUp':
next = current === 0 ? last : current - 1
break
default:
return
}
event.preventDefault()
this.check(this.#radios[next])
}
}
document.querySelectorAll('[role="radiogroup"][aria-activedescendant]')
.forEach((group) => new ActiveDescendantRadioGroup(group))Structure and Semantics
Assistive technology conveys the radio group's role, its accessible name, its set of options, and which of the options is checked if any. Either the native or ARIA construction conveys all four. They differ in how they convey them.
The native construction uses elements that convey role, name, and value, and that provide radio-group behavior without code.
- Radio buttons that share a
nameattribute form a group. Each input'scheckedproperty carries its state, itsvalueattribute carries its value, and its associated<label>sets an accessible name that identifies its value. - A
<fieldset>element around them has thegrouprole by default and takes its accessible name from its<legend>. You can addrole="radiogroup"to the<fieldset>so the group is reported as a radio group, but it isn't required. Without the<fieldset>, the inputs still behave as a group, but nothing names the question they answer.
| Attribute | How to use in native construction |
|---|---|
role="radiogroup" | Set on <fieldset> to declare radiogroup role rather than default group. (Optional) |
checked | Set in markup on one radio button to check it initially. (Optional) |
disabled | Set attribute or property when an option is not currently available. |
required | Set attribute on any of the radio buttons when one of them must be checked. |
The ARIA construction requires ARIA attributes and code, since generic elements convey no grouping or state. The container carries the radiogroup role and an accessible name, and each option carries the radio role, an accessible name, and an aria-checked attribute to declare its state. Every radio button is a descendant of the group in the DOM.
| Attribute | How to use in ARIA construction |
|---|---|
role="radiogroup" | Set on the group container. |
role="radio" | Set on each custom radio button that carries the interactive behavior. |
aria-checked | Set aria-checked="true" on the checked custom radio button and false on the rest. Leave it off native inputs, whose state is carried by checked. |
aria-labelledby | Set on the group container to the id of the visible text that names it. Set on a custom radio button whose name comes from text outside it. |
aria-label | Names a custom group or radio button when there's no visible text to reference. |
aria-describedby | Set on the group or a radio button to reference text that adds information, such as a hint or the reason the group isn't available. |
aria-required | Set to true on the group container when users must make a choice. |
aria-disabled | Set to true on a radio button that isn't available, or on the group container to declare the group disabled. |
tabindex | Using the roving tabindex technique, set tabindex="0" on the checked custom radio button and tabindex="-1" on the rest. Using the aria-activedescendant technique, set tabindex="0" on the group container. |
aria-activedescendant | Using the aria-activedescendant technique, set on the group container to the id of the checked radio button. |
Radio Group Has Radios
A radio group contains radio buttons: native <input type="radio"> elements, or elements with the radio role that are descendants of the group in the DOM. Assistive technology counts them to report how many options the group holds and which one users are on. A layout wrapper, such as a <div> around each radio button, is fine if it carries no role. At least one radio button is available to choose.
Gives members the required role
Reported as: No group members with required role
A custom radio group must contain elements with the radio role.
When the group's options are generic elements, or buttons without role="radio", assistive technology finds no radio buttons to report. Screen reader users are told they've entered a radio group, then hear its options as plain text, with no count and no checked state.
Give each option role="radio" and keep it inside the group in the DOM, or build the options from native radio inputs. Remove any role from a wrapper that sits between the group and its radio buttons.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Offers at least one choice
Reported as: No active radios
Sanity check. Evinced checks that the radio group has at least one radio button that isn't disabled.
A radio group should have at least one radio button that isn't disabled, or say why none is available.
When every option is disabled with no explanation, screen reader users can work through the whole group before finding that none of it responds, and they don't learn what would make it available.
Keep at least one option enabled when the group can be used. When it can't, disable the group as a whole and point its aria-describedby at a short explanation of what has to happen first.
Role
A radio group has roles at two levels: the container reports that it groups radio buttons, and each option reports that it's a radio button. A native <input type="radio"> has the radio role without any attribute, and a <fieldset> around the inputs has the group role, which serves as the container's. A custom group carries role="radiogroup" on the element that holds the radio buttons, and each option carries role="radio".
Has the correct role
Reported as: Element has incorrect role
A radio group's container must carry the radiogroup role, unless it's a <fieldset> around native radio buttons.
When a <div> holds the radio buttons with no role, assistive technology reports the options with nothing grouping them. Users aren't told that the options answer one question, or that choosing one replaces another.
Put role="radiogroup" on the element that holds the radio buttons, or build the group from a <fieldset> and native inputs.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Applies role to the correct element
Reported as: Incorrect root element
Sanity check. Evinced checks that the radiogroup role sits on the radio group itself, the element that holds the radio buttons, rather than on one of its descendants. This check runs on every UI pattern, not only on radio groups.
The radiogroup role should sit on the element that is the radio group, the one that holds the radio buttons and carries the group's name.
When the role sits on a wrapper inside the component while the name, the tabindex, or the key handling sits on its parent, assistive technology reports a group with no name, or a named element with no role. Users lose part of what identifies the group.
Put the role, the accessible name, and the key handling on one element, and keep any wrapper used for layout free of roles.
Accessibility Label
A radio group's name states the question its radio buttons answer, and screen reader users hear it as they move into the group, before any option. In the native construction, the <legend> names the <fieldset>. A custom group takes its name from visible text through aria-labelledby, or from aria-label when there's no visible text. Some screen readers repeat the group's name with every radio button, so keep it short.
Names a container that needs one
Reported as: Missing contextual labeling
A radio group must have an accessible name that states the question its radio buttons answer.
When the question sits in a heading or a paragraph above the group and nothing connects it to the group, screen reader users hear "Yes" and "No" with no indication of what they're answering. On a form with several groups, the groups can't be told apart.
Wrap native radio buttons in a <fieldset> and put the question in its <legend>. For a custom group, point aria-labelledby at the visible question, or use aria-label when there's none.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Radios Accessibility Labels
Each radio button has an accessible name that states its option and is unique within the group. A native radio button takes its name from a <label> wrapped around it or linked to it by for. A custom radio button takes its name from its text content, or from aria-labelledby or aria-label when it has none. Some screen readers don't repeat the group's name, so write names that make sense without it.
Identifies each member by name
Reported as: Group members missing accessible names
Every radio button in a group must have an accessible name that says what the option is.
When a radio button shows only an icon, a color swatch, or an image with empty alt text, or its label text isn't associated with it, screen reader users hear "radio button, 2 of 4" and nothing else. They can tell there are options but not what any of them are, so choosing one means guessing.
Give each radio button visible text, associated through a <label> for native inputs. When the design has no text, use aria-label, or meaningful alt text on the image inside a custom radio button.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Tells one member from another
Reported as: Group contains duplicate names
The radio buttons in one group should have different accessible names.
When two options share a name, such as two radio buttons named "Standard" for services at different prices, screen reader users can't tell them apart by name, and voice control users can't choose either one by saying it.
Put the distinguishing text into each name. When it's already on screen, such as a price beside the option, include it in the <label> or reference it with aria-labelledby.
Severity: Serious
Set Size
A radio button can state how many options its group holds with aria-setsize. Browsers count the radio buttons in the group, so most radio groups need neither it nor aria-posinset. When it's set, every radio button in the group carries the same value.
Reports one total for the set
Reported as: Aria-setsize conflicting values
Every radio button in a group must carry the same aria-setsize.
When the values differ, for example because options added later were given a new count, screen reader users hear the size of the group change as they move through it, from "2 of 3" to "3 of 4", with no way to tell which is right.
Write the total to every radio button from one source, and rewrite it on all of them when the options change.
Severity: Moderate
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Carries the total on each item
Reported as: Aria-setsize invalid implementation
aria-setsize should sit on each radio button rather than on the group.
When only the group carries the count, assistive technology looks for the total on the radio button users are on and finds none, so users aren't told how many options there are.
Set the same aria-setsize on every radio button, beside its aria-posinset, or remove both when every radio button is in the group's DOM.
Severity: Minor
Set Position
A radio button can state its position in the group with aria-posinset, which assistive technology reports alongside the total, for example as "2 of 4". Browsers work out the position and the total from the radio buttons in the group, so most radio groups don't need the attribute. Set it only when the group's radio buttons aren't all in the DOM, and then set aria-setsize beside it on every radio button.
Fits each position in the set
Reported as: Aria-posinset is larger than aria-setsize
A radio button's aria-posinset must be a whole number from 1 to the group's aria-setsize.
When positions start at 0, or come from a longer list than the size counts, screen reader users hear a position that can't be true, such as "5 of 4", and lose track of how many options remain.
Derive both values from the same list of radio buttons, and start the positions at 1. When every radio button is in the group's DOM, remove both attributes and let the browser count.
Severity: Moderate
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Carries the position on each item
Reported as: Aria-posinset invalid implementation
aria-posinset should sit on each radio button rather than on the group.
When the group carries the attribute, it states one position for a whole set of options, and the radio buttons state none. Assistive technology has nothing to report as users move from one option to the next.
Move the attribute onto each radio button, with aria-setsize beside it. When every radio button is in the group's DOM, remove the attribute instead.
Severity: Minor
aria-checked
Every radio button reports whether it's checked. A native radio button carries its state in the checked property, and the browser keeps it in step as users choose. A custom radio button carries aria-checked, set to true or false, and code updates it on every change.
Marks which member is checked
Reported as: No selection attributes
Every radio button in a custom radio group must carry aria-checked, set to true on the checked one and false on the others.
When the checked option is shown only by styling, screen reader users hear each option's name with no way to tell which one is chosen. They can't confirm that their choice took, or find out what was already set.
Set aria-checked on every radio button from the same value that draws the checked styling. When nothing is checked yet, set it to false on all of them.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Leaves native state to HTML
Reported as: Avoid aria-checked on native elements
A native radio button must not carry aria-checked.
When an <input type="radio"> also carries aria-checked, its state is kept in two places that can drift apart. Assistive technology reports the ARIA value while the form submits the native one, so users can be told that a radio button they just chose isn't checked.
Remove aria-checked from native inputs and let the checked property report the state. Keep aria-checked for radio buttons built from generic elements.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Picks one selection state
Reported as: Mixed selection attributes
A radio button should report its state with aria-checked alone.
When radio buttons carry aria-selected as well as aria-checked, one choice is recorded twice. The radio role doesn't support aria-selected, so assistive technology ignores it, and code that updates only aria-selected leaves screen reader users hearing a choice they've since changed.
Remove aria-selected from every radio button, and set aria-checked in one place.
Severity: Minor
Color Alone State Indication
A radio button shows that it's checked with a mark, not with color alone. A native radio button draws a filled dot inside its circle. A restyled or custom radio button needs a mark of its own, drawn from its checked or aria-checked state so the mark and the reported state change together.
Shows state by more than color
Reported as: State indicated by color alone
A radio button must show whether it's checked with a visual change other than color.
When the only difference between states is a fill color, such as a circle or an option card that turns the brand color when chosen, people with color vision deficiency or low vision, or people using a high-contrast display, can't tell which option is checked. Exposing the state to assistive technology doesn't help, because most of these users don't use any.
Draw a filled dot or a checkmark on the checked radio button, and keep any color change alongside it. Whatever tells the states apart needs a contrast of at least 3:1 with the colors around it. Draw the mark with an SVG, a border, or text rather than a CSS background color, which high-contrast modes can remove.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.4.1 Use of Color (A) | Color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element. |
Disabled
A radio group can be disabled as a whole, or one radio button at a time. Native radio buttons take the disabled attribute, and disabled on a <fieldset> disables every control inside it. Custom radio buttons and custom groups set aria-disabled="true", and code ignores clicks and key presses on them while it's set. Keep the checked radio button enabled, as described under Unreachable Group.
Disables only when role allows
Reported as: Component does not support disabled state
In a radio group, only the group and its radio buttons can declare a disabled state.
When disabled or aria-disabled sits on a <label>, on a wrapper around a radio button, or on the text that names the group, assistive technology may drop the state or report it on something users can't operate. The radio button inside stays available.
Move the state onto each radio button that isn't available, or onto the group or its <fieldset> when none of them is.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Disables only elements that support it
Reported as: Element type does not support disabled state
An element in a radio group must carry disabled or aria-disabled only when its role supports a disabled state.
When the state sits on an element whose role has none, such as a wrapper, a list item or a heading, assistive technology may drop it or report it on something users can't operate, while the control that really is unavailable is still reported as available.
Remove the attribute from that element, and set the disabled state on the control itself, using an element or role that supports it, such as a native <button>.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Avoids conflicting disabled states
Reported as: Conflicting disabled values
A radio button or radio group must declare its disabled state one way only: the disabled attribute on a native <input type="radio"> or <fieldset>, or aria-disabled on a custom radio button or group.
When a native radio button carries aria-disabled instead of disabled, it can still be checked while assistive technology reports it as disabled. When both attributes are set and disagree, assistive technology can report a state the browser doesn't enforce. Either way, screen reader users are told the wrong thing about whether the option is available.
Use disabled alone on native inputs and <fieldset> elements, and aria-disabled alone on custom radio buttons and groups, removing it or setting it to false when the option is available again.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Uses aria-disabled rather than disabled
Reported as: Element does not support the disabled attribute
A custom radio button or radio group must convey its disabled state with aria-disabled.
When a <div> with the radio or radiogroup role carries the disabled attribute, browsers ignore it, so the option stays available to assistive technology however it's styled. Users are told they can choose something the design has grayed out.
Set aria-disabled="true" instead, and have the click and key handlers ignore a radio button that carries it. When native radio buttons would do the job, switch to them and use disabled.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Forbidden Interactive Children
A radio button contains no interactive elements. The radio role makes its content presentational, so a link or button inside one loses its role and name. Keep other controls out of the group as well. Users moving through it with the arrow keys pass over anything that isn't a radio button, so place a link or a follow-up field after the group rather than between its radio buttons.
Avoids nested interactives
Reported as: Forbidden interactive children; Nested interactive elements
A radio button must not contain an interactive element, and a radio group must not hold controls other than its radio buttons.
When a radio button holds a link, such as "see rates" in an "Express shipping" option, assistive technology may drop the link's role or expose only the radio button, so screen reader users hear one control where two exist. When a button sits between two radio buttons, users moving with the arrow keys never reach it.
Move the link or button out of the radio button and out of the group, and place it just after the group.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Relationships
A radio group relates to its radio buttons as a parent to its children, and the DOM states that relationship without any attribute. Assistive technology counts the radio buttons inside the group and reports each one's position in it.
Native radio buttons are also tied together by their shared name attribute, which is what makes checking one uncheck the others. Keep every radio button that shares a name inside the same <fieldset>, so the group that screen reader users hear matches the group that behaves as one.
aria-owns
Keep radio buttons inside their group in the DOM. WAI-ARIA allows a radio group to claim radio buttons that sit elsewhere with aria-owns, but support for the attribute varies across assistive technologies, and focus and key handling still follow the DOM. Use it only when the markup can't be restructured, and then reference only elements with the radio role.
The following example shows a radio group that owns radio buttons rendered outside it.
<div
role="radiogroup"
aria-labelledby="contact-label"
aria-owns="contact-email contact-phone contact-mail">
<span id="contact-label">Preferred contact method</span>
</div>
<div
role="radio"
id="contact-email"
aria-checked="true"
tabindex="0">
Email
</div>
<div
role="radio"
id="contact-phone"
aria-checked="false"
tabindex="-1">
Phone
</div>
<div
role="radio"
id="contact-mail"
aria-checked="false"
tabindex="-1">
Mail
</div>Avoids aria-owns
Reported as: Avoid aria-owns
General check. Evinced checks that the radio group and its radio buttons don't use aria-owns to claim elements elsewhere in the document as their children. This check runs on every UI pattern, not only on radio groups.
A radio group should hold its radio buttons in the DOM rather than own them with aria-owns, and a radio button should own nothing.
When a group owns radio buttons rendered elsewhere, assistive technology that doesn't honor the attribute reports them outside any group, so screen reader users aren't told what question the options answer or how many there are. When a radio button owns content, that content becomes presentational, so it can vanish, be folded into the radio button's name, or be read twice.
Move the radio buttons into the group in the DOM, or render the whole group where they appear. Keep a radio button's content inside it, and reference a hint with aria-describedby.
Owns only conforming roles
Reported as: Invalid aria-owns values
General check. Evinced checks that every element the radio group references in aria-owns has a role the group may own. This check runs on several UI patterns.
When a radio group uses aria-owns, every element it references should have the radio role.
When the group owns a button, a text field, or plain content, assistive technology counts it among the options or reports it out of place, so the count and positions screen reader users hear stop matching the radio buttons on screen.
Reference only radio buttons, and each one from a single group. Better still, move them into the group in the DOM and remove aria-owns.
Operation
With a pointer, clicking or tapping a radio button, or its label, checks it and unchecks the one checked before. Clicking a checked radio button leaves it checked. Users change a radio group's answer by choosing another option, not by clearing the current one.
With a keyboard, a radio group is one stop in the tab order. The Tab key moves focus into the group, landing on the checked radio button or, when none is checked, on the first. The next press moves focus out of the group. Inside the group, the arrow keys move focus from one radio button to the next and check each one they reach, wrapping at either end.
Native radio buttons do all of this for you. Some browsers place focus on the last radio button rather than the first when users press Shift + Tab into a group with nothing checked, and that's acceptable. A custom group reproduces the behavior in code, managing focus with a roving tabindex or with aria-activedescendant, both described in Keyboard Navigation Inside Components.
| Key | Function |
|---|---|
| Tab | Moves focus into the group, onto the checked radio button or the first when none is checked, and on the next press out of the group |
| Shift + Tab | Moves focus into or out of the group in the reverse direction |
| Space | Checks the focused radio button when it isn't checked already |
| Right Arrow, Down Arrow | Moves focus to the next radio button, or from the last to the first, and checks it |
| Left Arrow, Up Arrow | Moves focus to the previous radio button, or from the first to the last, and checks it |
A radio group inside a toolbar, such as a set of text alignment options, works differently. In a typical horizontal toolbar, the Right Arrow and Left Arrow keys move focus from control to control without selecting anything, while the Space key, and optionally the Enter key, selects or activates the focused control.
A radio group in a toolbar behaves like the toolbar's other controls. From the last radio button in the group, focus moves on to the next control, and from the first, back to the previous control. Optionally, the Up Arrow and Down Arrow keys can wrap focus within the radio group.
Focus Sequence
A radio group is a single tab stop, and that stop lands on the checked radio button, or on the first when none is checked. Native radio buttons that share a name get this from the browser. A custom group moves tabindex="0" to each radio button as it's checked, or keeps the stop on the group itself with aria-activedescendant, as the Examples show.
Reachable with the Tab key
Reported as: Composite not in focus sequence
A radio group must have one element in the tab order, either one of its radio buttons or the group itself.
When every radio button carries tabindex="-1" and the group has no tabindex, or the radio buttons are generic elements with no tabindex at all, the Tab key passes over the group. Keyboard users can't reach any option, and the arrow keys only work once one of them has focus.
Give the checked radio button tabindex="0", or the first when none is checked, and the others tabindex="-1". Otherwise, give the group tabindex="0" and use aria-activedescendant.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Lands on the checked item
Reported as: Radio group unpredictable focus
When focus enters a radio group, it must land on the checked radio button, or on the first when none is checked.
When the tab stop stays on the first radio button after another is checked, keyboard users arrive on an option that isn't their choice, and screen reader users hear it as though it were the checked one. Their first arrow press then checks the option next to it, changing an answer they didn't mean to touch.
Move tabindex="0" to each radio button as it's checked, and set tabindex="-1" on the one that had it. Give native radio buttons in one group the same name.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Gives its items a single tab stop
Reported as: Redundant focusable children
A radio group must put no more than one radio button in the tab order, with the arrow keys reaching the rest.
When every radio button carries tabindex="0", or native inputs don't share a name, the Tab key stops on each option in turn. The options read as separate controls rather than answers to one question, and users press Tab through all of them to leave.
Give the checked radio button tabindex="0" and the rest tabindex="-1", and handle the arrow keys. Give native radio buttons in one group the same name.
Severity: Moderate
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Context Change
Checking a radio button changes only the group's answer. Anything more, such as submitting the form, loading another page, or moving focus, waits for an explicit control such as a submit button. This matters more in a radio group than in most controls: the arrow keys check each radio button they reach, so every step through the group is a change.
Keeps context when a value changes
Reported as: Unexpected context change on input
Checking a radio button must not change the context unless users are told beforehand that it will.
When checking an option loads another page or moves focus, keyboard users can't get past the first option they arrow to, and screen reader and screen magnifier users may not notice that the page changed. A form that submits partway through can discard what users hadn't finished entering.
Update the page in place and leave focus on the radio button. Start submission or navigation from an explicit control such as a Continue button, or, when the change of context is intended, say so in text that comes before the group.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 3.2.2 On Input (A) | Changing the setting of any user interface component does not automatically cause a change of context unless the user has been advised of the behavior before using the component. |
| EN 301 549 | 9.3.2.2 On Input | Same as the WCAG text. |
| Section 508 | 1194.22(n) | When electronic forms are designed to be completed on-line, the form shall allow people using assistive technology to access the information, field elements, and functionality required for completion and submission of the form, including all directions and cues. |
| Section 508 | 1194.21(l) | When electronic forms are used, the form shall allow people using assistive technology to access the information, field elements, and functionality required for completion and submission of the form, including all directions and cues. |
Keyboard Navigation
Inside a radio group, the arrow keys move focus from one radio button to the next and check each one they reach, wrapping at either end. The Right Arrow and Down Arrow keys move forward and the Left Arrow and Up Arrow keys move back, whether the options sit in a row or a column. For code that handles these keys, see the ARIA builds under Examples.
Keeps aria-activedescendant valid
Reported as: Missing active descendant
When a radio group manages focus with aria-activedescendant, the attribute must hold the id of the radio button that has focus, and change each time focus moves.
When the attribute keeps its first value, or names an id that no longer exists, the visible focus moves but screen reader users hear the old radio button, or none, and can't tell which option they've checked.
Give every radio button a unique id, and update aria-activedescendant in the same step that checks a radio button, on each arrow key and on a click.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Moves focus with the arrow keys
Reported as: Focus not updated with arrow keys
The arrow keys must move focus from one radio button to the next within a radio group.
When the radio buttons answer only clicks, an arrow press scrolls the page and focus stays where it landed. Keyboard users can't reach any option but the one holding the tab stop, so they can't change the answer.
Handle the arrow keys on the group, and move focus and the checked state one radio button per press, with a roving tabindex or aria-activedescendant. Call preventDefault so the page doesn't scroll.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Matches arrow keys to orientation
Reported as: Arrow key navigation failed
Sanity check. Evinced checks that pressing the arrow keys moves focus to the next radio button in the group. This check runs on several UI patterns.
A radio group should answer the arrow keys that match its layout, along with the other pair: the Right Arrow and Down Arrow keys move to the next radio button, and the Left Arrow and Up Arrow keys to the previous one.
When a row of radio buttons answers only the Up Arrow and Down Arrow keys, users press the key that matches what's on screen and nothing happens. They conclude the group has no keyboard support.
Handle all four arrow keys, whether the options sit in a row or a column.
Wraps focus at the ends
Reported as: Arrow key navigation loop failed
From the last radio button, the forward arrow keys should move focus to the first, and from the first, the backward arrow keys should move it to the last.
When focus stops at either end, a key press does nothing, which looks the same as a group whose keyboard support has stopped working. Users press again rather than turning back.
When focus is on the last radio button and users press the Right Arrow or Down Arrow key, move focus to the first radio button and check it. Mirror that from the first radio button for the Left Arrow and Up Arrow keys.
Severity: Minor
Manages focus across every item
Reported as: Skipping composite arrow key navigation
Sanity check. Evinced checks that each radio button in the group can take focus, so the arrow keys can reach it. This check runs on several UI patterns.
Every radio button in a group should be able to take focus in turn.
When a roving tabindex or aria-activedescendant covers only some radio buttons, such as those rendered first, the arrow keys stall partway through the group. Users can't tell whether they've reached the last option, and every option past the gap is out of reach.
Apply one focus method to every radio button, generated from the same list that renders them, so an option added later isn't left out.
Unreachable Group
Focus enters a radio group on the checked radio button, so that radio button can always take keyboard focus. It's never disabled and never taken out of the tab order. When its option stops being available, check another radio button before disabling it.
The following examples show the two ways a native group loses its tab stop. In both, Phone is the checked radio button. In the first, it carries tabindex="-1", and in the second, it's disabled. The examples set the checked attribute in the markup, but the group is just as unreachable when code checks Phone by setting the checked property.
<!-- Unreachable in some browsers: the checked radio button has tabindex="-1" -->
<fieldset>
<legend>Preferred contact method</legend>
<label>
<input
type="radio"
name="contact"
value="email">
Email
</label>
<label>
<input
type="radio"
name="contact"
value="phone"
checked
tabindex="-1">
Phone
</label>
</fieldset><!-- Unreachable in some browsers: the checked radio button is disabled -->
<fieldset>
<legend>Preferred contact method</legend>
<label>
<input
type="radio"
name="contact"
value="email">
Email
</label>
<label>
<input
type="radio"
name="contact"
value="phone"
checked
disabled>
Phone
</label>
</fieldset>Keeps the checked item reachable
Reported as: Unreachable group
The checked radio button in a group must be able to take keyboard focus.
When the checked radio button carries tabindex="-1" or is disabled, some browsers skip the group when users press Tab, so keyboard users can't reach any option, including the one shown as checked. Other browsers move focus to a different radio button. A disabled checked radio button also isn't submitted with the form, so the answer users see isn't the one the form sends.
Keep the checked radio button enabled and leave tabindex off it. When its option becomes unavailable, check another radio button before disabling it.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Checked Radios
Checking a radio button unchecks the one checked before, so a radio group holds one choice at most. A group can start with none checked, but once users check a radio button, the group always has one checked.
Users check a radio button by clicking it or its label, by pressing Space, or by moving to it with the arrow keys, and every route updates the state users are told about.
Selects from the keyboard
Reported as: No keyboard selection
A radio button must be checkable from the keyboard.
When checking is wired only to a click, keyboard users can move focus to an option but can't choose it. When a key moves focus without updating aria-checked, screen reader users hear the option they chose reported as unchecked.
Have Space and the arrow keys check a radio button by calling the same function a click calls, and update aria-checked in that function.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Honors a single-selection model
Reported as: Multiple selection not supported
A radio group must have no more than one radio button checked.
When checking a radio button doesn't uncheck the previous one, or the group loads with two checked, users are told the group holds two answers at once and can't tell which one the form will send. The screen usually shows only one, so only people relying on the reported state encounter the contradiction.
Derive every radio button's aria-checked value from one stored choice, so checking one clears the rest. When users can choose several options, use checkboxes instead.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Acts on Space when focused
Reported as: Spacebar does not trigger selection
Pressing Space on a focused radio button must check it.
When nothing is checked yet, focus enters the group on the first radio button without checking it, and Space is the only key that checks it without moving away. When Space does nothing, or scrolls the page instead, keyboard users can't choose that option and may lose sight of the group.
Handle Space on the radio buttons, check the focused one through the same function a click uses, and call preventDefault so the page doesn't scroll.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Event Listener Type
When script in a radio group responds to keys, it listens for keydown or keyup. The keypress event is deprecated, and browsers don't fire it for many keys, including the arrow keys, Home, End and Escape.
Avoids the keypress event
Reported as: Deprecated keypress listener
A radio group's key handling should use keydown or keyup, not keypress.
When a handler listens for keypress, keys the browser doesn't send through that event go unanswered, and keyboard users find the radio group ignoring keys it's meant to respond to.
Replace each keypress listener with a keydown listener, and read the key from event.key.
Severity: Minor
FAQ
Which WCAG success criteria do the radio group issues relate to?
Most relate to 4.1.2 Name, Role, Value: the group's role, each radio button's name and checked state, keeping to one checked radio button, how the group and its radio buttons convey a disabled state, and keeping other interactive elements out of them.
Naming the group, the set size and positions, the radio buttons' role, a single tab stop, and aria-activedescendant relate to 1.3.1 Info and Relationships.
Reaching the group, landing on the checked radio button, and checking and moving between radio buttons with the keyboard relate to 2.1.1 Keyboard. Showing the checked state by more than color relates to 1.4.1 Use of Color, and keeping the context when a radio button is checked to 3.2.2 On Input.
Distinct radio button names, aria-checked without aria-selected, setting the size and position on each radio button, and wrapping focus at the ends map to no WCAG success criterion and are best practices.
Why are some radio group issues marked as Critical and others as Minor?
Severity reflects how much an issue gets in users' way. Critical issues stop users outright: a group with no role, radio buttons with no name or reported state, or a group keyboard users can't reach, check, or move through.
Serious issues confuse users though tasks are still possible, such as an unnamed group, duplicate names, two radio buttons checked at once, a wrong disabled state, showing the state by color alone, or changing the context without warning.
A set size or position that can't be true, or a tab stop on every radio button, is Moderate. Recording the state in aria-selected as well, setting the size or position on the group, or focus that stops at the ends is Minor.
The aria-owns checks and the checks on where the role sits, whether any radio button is enabled, and arrow-key focus carry no severity: Evinced reports them as general and sanity checks.
Related Patterns
- Checkbox: marks an option or set of options as chosen or not, any number at a time
- Switch: turns a single feature or setting on or off
- Listbox: a list of options to select from, suited to longer lists
- Combobox: a control that opens a list of values to select
- Button: performs an action or command rather than recording a choice
- Tablist: a set of tabs that selects which content panel to display