React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.
In this article, we’ll look at how to create accessible React apps.
Why Accessibility?
We should create accessible apps so that they can be used by everyone.
Standards and Guidelines
The Web Content Accessibility Guidelines provide guidelines for creating accessible web sites.
Also, we have the Web Accessibility Initiative — Accessible Rich Internet Applications document contains guidelines for building fully accessible JavaScript widgets.
aria-*
HTML attributes are fully supported in JSX. These attributes are kept as kebab-case as they’re in HTML.
Semantic HTML
HTML tags are named for their meanings. This means that they make sense for screen readers and other accessibility programs that parse web content.
This means that instead of using div
to group elements, we should use Fragments instead so that our app doesn’t have extra div
elements getting in the way of screen readers.
For example, we can add fragments to a component as follows:
function ListItem({ item }) {
return (
<Fragment>
<dt>{item.term}</dt>
<dd>{item.meaning}</dd>
</Fragment>
);
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [
{ term: "coffee", meaning: "black drink" },
{ term: "milk", meaning: "white drink" }
]
};
}
render() {
return (
<dl>
{this.state.items.map(item => (
<ListItem item={item} key={item.id} />
))}
</dl>
);
}
}
Then we get the following HTML rendered:
<dl>
<dt>coffee</dt>
<dd>black drink</dd>
<dt>milk</dt>
<dd>white drink</dd>
</dl>
As we can see, fragments do not produce any extra HTML but let us group elements together.
A shorthand or fragments are <>
and </>
. Therefore, we can rewrite the code above as follows:
function ListItem({ item }) {
return (
<>
<dt>{item.term}</dt>
<dd>{item.meaning}</dd>
</>
);
}
We keep the rest of the code the same.
Then we get the same HTML rendered.
Accessible Forms
We should label forms to provide accessibility for all users.
To do this, we add a for
attribute to label
elements. In React, the for
element represented by the htmlFor
prop.
This applies to all form controls.
For example, we can do the following to add a htmlFor
prop:
class App extends React.Component {
constructor(props) {
super(props);
this.state = { name: "" };
}
render() {
return (
<>
<label htmlFor="name">Name:</label>
<input type="text" name="name" value={this.state.name} />
</>
);
}
}
In the code above, we have:
htmlFor="name"
to relate the label to the form field. The htmlFor
value should be the same as the name
value of the form control element.
The full accessibility guidelines are at:
- The W3C shows us how to label elements
- WebAIM shows us how to label elements
- The Paciello Group explains accessible names
Notifying the user of errors
Errors need to understood by users. The following guides tells us how to expose error text to screen readers:
Focus Control
We should only change or remove outlines by using CSS so that users can get the focused element even without seeing the outline.
Ways to Skip to Desired Content
Also, we should provide ways for users to skip to what they want to see or use.
We can implement that with internal page anchors and some styling.
We can also put important content in elements like main
and aside
so that they can be differentiated from other content.
Programmatically Managing Focus
In React, we can access DOM elements by using refs. This means that we can control when an element is in focus by calling DOM methods to focus an element.
For example, if we want to focus an element when the component is mounted in the DOM tree, we can call focus
as follows:
class App extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
componentDidMount() {
this.textInput.current.focus();
}
render() {
return <input type="text" ref={this.textInput} />;
}
}
In the code above, we created a ref
to access the DOM element by writing:
this.textInput = React.createRef();
In the input
element, we have:
ref={this.textInput}
to associate the input
element with the ref
we created.
Then in the componentDidMount
hook, we have:
this.textInput.current.focus();
to focus the input when the component mounts. this.textInput.current
returns the input element, so that we can call the native focus
method on it.
Conclusion
When creating React apps, we should have accessibility in mind.
This means that we should use semantic HTML tags so that all users can understand what a page has.
Also, we should provide ways for users to skip to the content that they want to see.
Finally, we can focus elements programmatically so that users can use inputs right away when focus is lost by regaining focus.