https://github.com/palmerhq/radio-group
845 byte WAI-ARIA 1.1 compliant radio group React component
https://github.com/palmerhq/radio-group
a11y formik forms radio-buttons react react-dom wai-aria
Last synced: about 1 month ago
JSON representation
845 byte WAI-ARIA 1.1 compliant radio group React component
- Host: GitHub
- URL: https://github.com/palmerhq/radio-group
- Owner: palmerhq
- License: mit
- Created: 2019-03-15T20:51:05.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2023-01-07T03:51:02.000Z (almost 3 years ago)
- Last Synced: 2025-05-07T00:06:26.742Z (5 months ago)
- Topics: a11y, formik, forms, radio-buttons, react, react-dom, wai-aria
- Language: TypeScript
- Homepage: https://codesandbox.io/embed/qxxnwzvy0w
- Size: 2.18 MB
- Stars: 134
- Watchers: 3
- Forks: 10
- Open Issues: 30
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# `@palmerhq/radio-group`
An accessible [WAI-ARIA 1.1-compliant Radio Group](https://www.w3.org/TR/wai-aria-practices-1.1/#radiobutton) React component.
- [`@palmerhq/radio-group`](#palmerhqradio-group)
- [Installation](#installation)
- [Usage](#usage)
- [Usage with Formik v2](#usage-with-formik-v2)
- [API Reference](#api-reference)
- [``](#radiogroup-)
- [`labelledBy?: string`](#labelledby-string)
- [`onChange: (value: any) => void`](#onchange-value-any--void)
- [`children: React.ComponentType[]`](#children-reactcomponenttyperadioprops)
- [`value: any`](#value-any)
- [`as?: React.ComponentType`](#as-reactcomponenttype)
- [`autoFocus?: boolean`](#autofocus-boolean)
- [``](#radio)
- [`value: any`](#value-any-1)
- [`onFocus?: () => void`](#onfocus---void)
- [`onBlur?: () => void`](#onblur---void)
- [`as?: React.ComponentType`](#as-reactcomponenttype-1)
- [Underlying DOM Structure](#underlying-dom-structure)
- [Overriding Styles](#overriding-styles)
- [Accessibility Features](#accessibility-features)
- [Authors](#authors)## Installation
```
yarn add @palmerhq/radio-group
```Or try it out in your browser on [CodeSandbox](https://codesandbox.io/embed/qxxnwzvy0w)
> Note: This package uses `Array.prototype.findIndex`, so be sure that you have properly polyfilled.
## Usage
```tsx
import * as React from 'react';
import { RadioGroup, Radio } from '@palmerhq/radio-group';
import '@palmerhq/radio-group/styles.css'; // use the default stylesfunction App() {
const [value, setValue] = React.useState();return (
<>
Color
setValue(value)}
>
Blue
Red
Green
>
);
}
```### Usage with Formik v2
```tsx
import * as React from 'react';
import { Formik, Form, useField } from 'formik';
import { RadioGroup, Radio } from '@palmerhq/radio-group';
import '@palmerhq/radio-group/styles.css'; // use the default stylesfunction FRadioGroup(props) {
const [{ onChange, onBlur, ...field }] = useField(props.name);
return (
);
}function App() {
return (
{
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 500);
}}
>
Color
Blue
Red
Green
);
}
```## API Reference
### ``
This renders a `div` and will pass through all props to the DOM element. It's children must be `` components.
#### `labelledBy?: string`
This should match the `id` you used to label the radio group.
```tsx
Color
{/* ... */}
```
#### `onChange: (value: any) => void`
A callback function that will be fired with the `value` of the newly selected item.
```tsx
import * as React from 'react';
import { RadioGroup, Radio } from '@palmerhq/radio-group';
import '@palmerhq/radio-group/styles.css'; // use the default stylesfunction App() {
const [value, setValue] = React.useState();return (
<>
Color
setValue(value)}
>
Blue
Red
Green
>
);
}
```#### `children: React.ComponentType[]`
**Required**
The children of a `` can **ONLY** be `` components. In order to support compliant keyboard behavior, each sibling must know the value of the whole group and so `React.Children.map` is used internally.
```tsx
Color
{/* ... */}
```
#### `value: any`
**Required**
The current value of the radio group. This is shallowly compared to each `value` prop of the child `` components to determine which item is active.
#### `as?: React.ComponentType`
Component to use a the wrapper. Default is `
`.#### `autoFocus?: boolean`
Whether to autoFocus the selected radio option.
### ``
This renders a `div` with a data attribute `data-palmerhq-radio` and all the relevant perfect aria attributes. The React component will pass through all props to the DOM element.
#### `value: any`
**Required**
The value of the radio button. This will be set / passed back to the `` when the item is selected.
#### `onFocus?: () => void`
Callback function for when the item is focused. When focused, a data attribute `data-palmerhq-radio-focus` is set to `"true"`. You can thus apply the selector to manage focus style like so:
```css
[data-palmerhq-radio][data-palmerhq-radio-focus='true'] {
background: blue;
}
```#### `onBlur?: () => void`
Callback function for when the item is blurred
#### `as?: React.ComponentType`
Component to use as radio. Default is `
`.### Underlying DOM Structure
For reference, the underlying HTML DOM structure are all `div`s and looks as follows.
```html
Red
Green
Blue
```### Overriding Styles
These are the default styles. Copy and paste the following into your app to customize them.
```css
[data-palmerhq-radio-group] {
padding: 0;
margin: 0;
list-style: none;
}[data-palmerhq-radio-group]:focus {
outline: none;
}[data-palmerhq-radio] {
border: 2px solid transparent;
border-radius: 5px;
display: inline-block;
position: relative;
padding: 0.125em;
padding-left: 1.5em;
padding-right: 0.5em;
cursor: default;
outline: none;
}[data-palmerhq-radio] + [data-palmerhq-radio] {
margin-left: 1em;
}[data-palmerhq-radio]::before,
[data-palmerhq-radio]::after {
position: absolute;
top: 50%;
left: 7px;
transform: translate(-20%, -50%);
content: '';
}[data-palmerhq-radio]::before {
width: 14px;
height: 14px;
border: 1px solid hsl(0, 0%, 66%);
border-radius: 100%;
background-image: linear-gradient(to bottom, hsl(300, 3%, 93%), #fff 60%);
}[data-palmerhq-radio]:active::before {
background-image: linear-gradient(
to bottom,
hsl(300, 3%, 73%),
hsl(300, 3%, 93%)
);
}[data-palmerhq-radio][aria-checked='true']::before {
border-color: hsl(216, 80%, 50%);
background: hsl(217, 95%, 68%);
background-image: linear-gradient(
to bottom,
hsl(217, 95%, 68%),
hsl(216, 80%, 57%)
);
}[data-palmerhq-radio][aria-checked='true']::after {
display: block;
border: 0.1875em solid #fff;
border-radius: 100%;
transform: translate(25%, -50%);
}[data-palmerhq-radio][aria-checked='mixed']:active::before,
[data-palmerhq-radio][aria-checked='true']:active::before {
background-image: linear-gradient(
to bottom,
hsl(216, 80%, 57%),
hsl(217, 95%, 68%) 60%
);
}[data-palmerhq-radio]:hover::before {
border-color: hsl(216, 94%, 65%);
}[data-palmerhq-radio][data-palmerhq-radio-focus='true'] {
border-color: hsl(216, 94%, 73%);
background-color: hsl(216, 80%, 97%);
}[data-palmerhq-radio]:hover {
background-color: hsl(216, 80%, 92%);
}
```## Accessibility Features
- Uses CSS attribute selectors for synchronizing `aria-checked` state with the visual state indicator.
- Uses CSS `:hover` and `:focus` pseudo-selectors for styling visual keyboard focus and hover.
- Focus indicator encompasses both radio button and label, making it easier to perceive which option is being chosen.
- Hover changes background of both radio button and label, making it easier to perceive that clicking either the label or button will activate the radio button.Keyboard Support
Key
Function
Tab
- Moves focus to the checked
radio
button in theradiogroup
.
- If a
radio
button is not checked, focus moves to the firstradio
button in the group.
Space
- If the
radio
button with focus is not checked, changes the state tochecked
.
- Otherwise, does nothing.
- Note: The state where a radio is not checked only occurs on page load.
Right arrow
- Moves focus to and checks the next
radio
button in the group.
- If focus is on the last
radio
button, moves focus to the firstradio
button.
- The state of the previously checked radio button is changed to unchecked.
Down arrow
- Moves focus to and checks the next
radio
button in the group.
- If focus is on the last
radio
button, moves focus to the firstradio
button.
- The state of the previously checked radio button is changed to unchecked.
Left arrow
- Moves focus to and checks the previous
radio
button in the group.
- If focus is on the first
radio
button, moves focus to and checks the lastradio
button.
- The state of the previously checked radio button is changed to unchecked.
Up arrow
- Moves focus to and checks the previous
radio
button in the group.
- If focus is on the first
radio
button, moves focus to and checks the lastradio
button.
- The state of the previously checked radio button is changed to unchecked.
Role, Property, State, and Tabindex Attributes
Role
Attributes
Element
Usage
radiogroup
div
- Identifies the
div
element as a container for a group ofradio
buttons.
- Is not focusable because focus is managed using a roving tabindex strategy as described below.
aria-labelledby="[IDREF]"
div
Refers to the element that contains the label of the radio group.
radio
div
- Identifies the
div
element as an ARIAradio
button.
- The accessible name is computed from the child text content of the
div
element.
tabindex="-1"
div
- Makes the element focusable but not part of the page Tab sequence.
- Applied to all radio buttons contained in the radio group except for one that is included in the page Tab sequence.
- This approach to managing focus is described in the section on roving tabindex.
tabindex="0"
div
- Makes the radio button focusable and includes it in the page Tab sequence.
- Set on only one radio in the radio group.
- On page load, is set on the first radio button in the radio group.
- Moves with focus inside the radio group so the most recently focused radio button is included in the page Tab sequence.
- This approach to managing focus is described in the section on roving tabindex.
aria-checked="false"
div
- Identifies
radio
buttons which are not checked.
- CSS attribute selectors (e.g.
[aria-checked="false"]
) are used to synchronize the visual states with the value of thearia-checked
attribute.
- The CSS
::before
pseudo-class is used to indicate visual state of unchecked radio buttons to support high contrast settings in operating systems and browsers.
aria-checked="true"
div
- Identifies the
radio
button which is checked.
- CSS attribute selectors (e.g.
[aria-checked="true"]
) are used to synchronize the visual states with the value of thearia-checked
attribute.
- The CSS
::before
pseudo-class is used to indicate visual state of checked radio buttons to support high contrast settings in operating systems and browsers.
## Authors
- Jared Palmer ([@jaredpalmer](https://twitter.com/jaredpalmer))
---
> MIT License