Radio and Radio Group
Radio and Radio Group
Overview
It is always best to provide native HTML radio buttons, rather than custom radio buttons, but when coded correctly, the custom buttons can and should act like native HTML radio buttons.
Turn on a screen reader to experience this example in action.
Which one of these scientists most inspires you?
Marie Curie
Jane Goodall
Rosalind Franklin
Hedy Lamarr
HTML Source Code
<!--div role="radiogroup" aria-labelledby="group_label_1" id="rg1">
<h2 id="group_label_1">What's your favorite framework?</h2>
<div role="radio" aria-checked="false" tabindex="0">React</div>
<div role="radio" aria-checked="false" tabindex="-1">Angular</div>
<div role="radio" aria-checked="false" tabindex="-1">Vue</div>
<div role="radio" aria-checked="false" tabindex="-1">Ember</div>
<div role="radio" aria-checked="false" tabindex="-1">None</div>
</div-->
<div class="dqu-example">
<div >
<!--h2 id="group_label_1">What's your favorite framework?</h2-->
<fieldset>
<legend>What's your favorite framework?</legend>
<input type="radio" id="react" name="fav_language" value="React">
<label for="react">React</label><br>
<input type="radio" id="angular" name="fav_language" value="Angular">
<label for="angular">Angular</label><br>
<input type="radio" id="vue" name="fav_language" value="Vue">
<label for="vue">Vue</label><br>
<input type="radio" id="none" name="fav_language" value="None">
<label for="none">None</label><br>
</fieldset>
</div>
<div role="radiogroup" aria-labelledby="group_label_2" id="rg2">
<h3 id="group_label_2">Which one of these scientists most inspires you?</h3>
<div role="radio" aria-checked="false" tabindex="0">Marie Curie</div>
<div role="radio" aria-checked="false" tabindex="-1">Jane Goodall</div>
<div role="radio" aria-checked="false" tabindex="-1">Rosalind Franklin</div>
<div role="radio" aria-checked="false" tabindex="-1">Hedy Lamarr</div>
</div>
<!---
This component has been adapted from an example provided by the W3C, in accordance with the W3C Software and Document License https://www.w3.org/copyright/software-license-2023/
-->
</div>
JavaScript Source Code
class RadioGroup {
constructor(groupNode) {
this.groupNode = groupNode;
this.radioButtons = [];
this.firstRadioButton = null;
this.lastRadioButton = null;
var rbs = this.groupNode.querySelectorAll('[role=radio]');
for (var i = 0; i < rbs.length; i++) {
var rb = rbs[i];
rb.tabIndex = -1;
rb.setAttribute('aria-checked', 'false');
rb.addEventListener('keydown', this.handleKeydown.bind(this));
rb.addEventListener('click', this.handleClick.bind(this));
rb.addEventListener('focus', this.handleFocus.bind(this));
rb.addEventListener('blur', this.handleBlur.bind(this));
this.radioButtons.push(rb);
if (!this.firstRadioButton) {
this.firstRadioButton = rb;
}
this.lastRadioButton = rb;
}
this.firstRadioButton.tabIndex = 0;
}
setChecked(currentItem) {
for (var i = 0; i < this.radioButtons.length; i++) {
var rb = this.radioButtons[i];
rb.setAttribute('aria-checked', 'false');
rb.tabIndex = -1;
}
currentItem.setAttribute('aria-checked', 'true');
currentItem.tabIndex = 0;
currentItem.focus();
}
setCheckedToPreviousItem(currentItem) {
var index;
if (currentItem === this.firstRadioButton) {
this.setChecked(this.lastRadioButton);
} else {
index = this.radioButtons.indexOf(currentItem);
this.setChecked(this.radioButtons[index - 1]);
}
}
setCheckedToNextItem(currentItem) {
var index;
if (currentItem === this.lastRadioButton) {
this.setChecked(this.firstRadioButton);
} else {
index = this.radioButtons.indexOf(currentItem);
this.setChecked(this.radioButtons[index + 1]);
}
}
/* EVENT HANDLERS */
handleKeydown(event) {
var tgt = event.currentTarget,
flag = false;
switch (event.key) {
case ' ':
this.setChecked(tgt);
flag = true;
break;
case 'Up':
case 'ArrowUp':
case 'Left':
case 'ArrowLeft':
this.setCheckedToPreviousItem(tgt);
flag = true;
break;
case 'Down':
case 'ArrowDown':
case 'Right':
case 'ArrowRight':
this.setCheckedToNextItem(tgt);
flag = true;
break;
default:
break;
}
if (flag) {
event.stopPropagation();
event.preventDefault();
}
}
handleClick(event) {
this.setChecked(event.currentTarget);
}
handleFocus(event) {
event.currentTarget.classList.add('focus');
}
handleBlur(event) {
event.currentTarget.classList.remove('focus');
}
}
// Initialize radio button group
window.addEventListener('load', function () {
var radios = document.querySelectorAll('[role="radiogroup"]');
for (var i = 0; i < radios.length; i++) {
new RadioGroup(radios[i]);
}
});
//This component has been adapted from an example provided by the W3C, in accordance with the W3C Software and Document License https://www.w3.org/copyright/software-license-2023/
CSS Source Code
/*
Radio and Radio Group — Restyled
Deque University ARIA Component
This component has been adapted from an example provided by the W3C,
in accordance with the W3C Software and Document License
https://www.w3.org/copyright/software-license-2023/
*/
:root {
--dqu-interactive: #2e5f7a;
--dqu-interactive-light: rgba(46, 95, 122, 0.08);
--dqu-border-secondary: #8c827d;
--dqu-text-primary: #21201e;
--dqu-font-family: "Noto Sans", sans-serif;
}
[role="radiogroup"] {
padding: 0;
margin: 0;
list-style: none;
}
[role="radiogroup"]:focus {
outline: none !important;
}
[role="radio"] {
padding: 4px 8px;
border: 2px solid transparent;
border-radius: 4px;
display: inline-block;
position: relative;
cursor: default;
outline: none;
color: var(--dqu-text-primary);
font-family: var(--dqu-font-family);
font-size: 1rem;
line-height: 1.5;
}
[role="radio"]::before {
position: relative;
top: 3px;
margin: 4px;
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='16' width='16' style='forced-color-adjust: auto;'%3E%3Ccircle cx='8' cy='8' r='7' stroke='%232e5f7a' stroke-width='2' fill-opacity='0' /%3E%3C/svg%3E");
}
[role="radio"][aria-checked="true"]::before {
position: relative;
top: 3px;
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='16' width='16' style='forced-color-adjust: auto;'%3E%3Ccircle cx='8' cy='8' r='7' stroke='%232e5f7a' stroke-width='2' fill-opacity='0' /%3E%3Ccircle cx='8' cy='8' r='4' fill='%232e5f7a' stroke-opacity='0' /%3E%3C/svg%3E");
}
[role="radio"].focus,
[role="radio"]:hover {
border: 2px solid var(--dqu-interactive);
background-color: var(--dqu-interactive-light);
cursor: pointer;
}
/* Native radio inputs */
input[type="radio"] {
appearance: none;
-webkit-appearance: none;
width: 16px;
height: 16px;
border: 2px solid var(--dqu-interactive);
border-radius: 50%;
background: #ffffff;
vertical-align: middle;
margin: 0 4px;
cursor: pointer;
position: relative;
}
input[type="radio"]:checked {
border-color: var(--dqu-interactive);
}
input[type="radio"]:checked::after {
content: "";
position: absolute;
top: 3px;
left: 3px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--dqu-interactive);
}
input[type="radio"]:focus-visible {
outline: 3px solid var(--dqu-interactive) !important;
outline-offset: 2px;
}
input[type="radio"]:hover {
outline: 3px solid var(--dqu-interactive);
outline-offset: 2px;
}
/* Fieldset styling for native radio group */
.dqu-example fieldset {
border: 1px solid var(--dqu-border-secondary);
border-radius: 8px;
padding: 12px 16px;
font-family: var(--dqu-font-family);
line-height: 2.2;
}
.dqu-example fieldset legend {
font-weight: 600;
font-size: 1rem;
color: var(--dqu-text-primary);
padding: 0 4px;
}
.dqu-example fieldset label {
font-family: var(--dqu-font-family);
font-size: 1rem;
color: var(--dqu-text-primary);
cursor: pointer;
margin-left: 4px;
}
.radio-container {
display: block;
}