Creating platforms for the web has always been as simple as learning to write HTML, throwing in look and feel with some CSS, and then, possibly a bit of JavaScript or any other scripting language for functionality.
However, with the advent of JavaScript component frameworks, a lot of concepts have come into play, such as styled-components and JSX(React) (along with many others). The most interesting part of these frameworks is breaking down webpages into specific components and importing them only when they are needed by the interface from the user.
This begs the questions, does this affect the way the web works? Does it make it better? Can we still write semantic code? Does the single entry point into the component make it hard to get an accessible DOM tree? Let’s find out.
In this article, we will be implementing accessibility features in ReactJS applications. We will look at how to achieve this in component-based applications while avoiding unnecessary div importations using React Fragments. We will also be looking at the concept of focus management and how ref
helps to implement this in ReactJS.
Before we go any further, this article assumes the following:
Making a web project accessible can feel overwhelming, but it’s really just as simple as implementing a more semantic approach to writing code to enable all users. The foundation for this is the POUR principle which guides building accessible websites.
Simply put POUR means – Perceivable, Operable, Understandable, Robust.
Perceivable: This means the web should be available to the senses (vision, touch, and hearing) either through the browser or through assistive technologies like screen readers and screen enlargers.
Operable: This means users can interact with all controls and interactive elements using either the mouse, keyboard or an assistive device. We will look at making platforms operable in the section on focus management.
Understandable: In this aspect, we consider the use of language which includes trying as much as possible to cut down spelling errors and complex grammar.
Robust: This means having consistency across the web. What this means is your platform must work the same way across all platforms.
We’ve heard this question a lot. The reason for this is that React applications rely on what is called a virtual DOM which is built every time a part of the application has to re-render because of a change. Breaking down components only accept a single root element (mostly a div) this is not semantic and will not be recognized by an accessibility tool like a screen reader.
However, accessibility in React can be achieved in a number of ways that will be discussed in this article.
To achieve semantic JSX, there are a few tools and practices that can help to make your React application more user-friendly. We’ll be looking at these tools and practices in this section.
Before we take a comprehensive look at why React Fragments is important in making React accessible, let’s understand how semantic JSX was achieved before React Fragments in order to appreciate its importance.
1. Importing elements using Div
tag
Normally when learning to build React applications we learn to wrap code for a particular component in a div
or span
tag. Most React developers use the div
tag to ensure the code gets to its imported location as a block. Now, all this does is return a div
inside another div
, and this prevents writing semantic JSX.
Drawbacks of this method
When handling importations like this, what we are doing is building React’s Virtual Dom with div
tags that would eventually be rendered to the main DOM as non-semantic HTML which makes it difficult for screen readers to interpret.
2. Using array as entry points
Since React components only return a single root element into another parent component, we can decide to map out the information in the child component to the parent component by returning an array of values like so:
import React from 'react' function NameList() { return ( [ <h2 key="1"> List of Developers in Nigeria </h2>, <li key="2"> Obinna </li> <li key="3"> Kofo </li> <li key="4"> Jola </li> ] ) } export default NameList
This works, although it leads to rendering extra elements in the DOM which might not be needed. Also having to always map out an array of elements that would have unique keys can be seen as a lot of syntax usage.
To solve the problem of unnecessary importations, a solution was introduced in React v16.8. Fragments helps you group a list of children without adding extra nodes to the DOM. Basically what fragments does is helps to guaranty a more semantic importation of child components to parent components by creating the virtual DOM exactly as we write it.
For instance, a simple use case would be calling a <td>
(table data) tag in a <tr>
(table roll) using old syntax:
class Table extends React.Component { render() { return ( <table> <tr> <Columns /> </tr> </table> ); } }
In the columns component, we would have this:
class Columns extends React.Component { render() { return ( <div> <td>Hello</td> <td>World</td> </div> ); } }
But to avoid invalid HTML errors, the <Columns />
would need to return multiple <td>
elements instead of a div
when rendered. The output of the above would look like this:
<table> <tr> <div> <td>Hello</td> <td>World</td> </div> </tr> </table>
With fragments, this is solved like so:
class Columns extends React.Component { render() { return ( <React.Fragment> <td>Hello</td> <td>World</td> </React.Fragment> ); } }
Fragments can be used like this <React.Fragment>
or by using empty tags <>
.
<React.Fragment>
tag for nowThe obvious way React Fragments helps to improve semantic JSX is by eliminating unnecessary div
tags in the DOM tree, it also allows us to write more semantic HTML which, as stated earlier, is the bases of accessible code. Other tips to achieving accessibility include:
div
to an article/section can make a huge differenceFocus refers to the control from the computer screen that receives input when you send information, this is usually associated with the keyboard. Whenever you attempt to fill a form or use a specific part of a web page, you have to put it in focus. Now, this is important to users who would rather navigate the platform with the keyboard using tab
and shift
keys or have some sort of motor disability.
Well planned focus management is important in ensuring a comfortable user experience. What this means is moving the cursor from one part of the app to another. In order to help users (mostly with motor disabilities) navigating the platform with the keyboard in the intended flow of the app. Some elements are implicitly focus-able such as form elements, anchor elements while others are not (such as p
, h2
tags).
ref
In order to focus an element using React we create a function Ref, which is a prop that is set on the element we want to reference, it allows us to select and reference an actual DOM node on the page in React.
<div ref ={ (loadingNames)=> { this.loadingNames = loadingNames; } } tabIndex = "-1" > Loading List of Names... </div>
The above code assigns the ref
of the div to the class property this.loadingNames
to implement a ref we use the componentDidMount life cycle, then call the focus element of the ref
element like this:
componentDidMount(){ this.loadingNames.focus() }
So what this does is, when the list of names loads, the keyboard focus indicator will put a focus ring on the content.
Another use case for using ref
would be making sure that we shift focus to the new page when using react-router, by calling a ref
for the top of the page and causing the user to navigate from the top of the new page the <link>
connects to.
<div ref={ (topOfNewPage)=>{ this.topOfNewPage = topOfNewPage; } } tabIndex = "-1" aria-labelledby = "pageHeading" > <Header / > <h1 id ="pageHeading"> </h1> <Footer/> </div>
Using the ref like so:
componentDidMount(){ this.topOfNewPage.focus() }
With the release of React v16.8 there is a way to write refs
using React.createRef()
API. You can find more information in the official documentation.
This helps to increase SEO, and also can update the content in the browser tab, it also enables screen readers to have a better understanding of their current place in the application.
componentDidMount(){ document.title = 'Input your page title ' }
Other ways to do this is by using packages like react-document-title and react-helmet which can be installed to the project via NPM.
Using a linter helps to determine clean code by checking code written by code standards determined by a team or by an individual. Using the eslint-plugin-jsx-a11y linter we can set up a more accessible react app. You can also configure this to work with your text-editor so as to get real-time error outputs. It can also be installed using NPM.
npm install eslint esline-plugin-jsx-a11y --save-dev
Mostly writing code that is semantic can go a long way in aiding a more accessible platform, earlier in the article I stated ways to achieve this. Also referring to this guide can help with this.
In this article, we have attempted to understand the basics of accessibility for the web as it applies to React applications and also looked at some accessibility principles in general. I hope we put these practices into play when building React applications to enable a better web for everyone. Happy Coding! 😄
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowSOLID principles help us keep code flexible. In this article, we’ll examine all of those principles and their implementation using JavaScript.
JavaScript’s Date API has many limitations. Explore alternative libraries like Moment.js, date-fns, and the new Temporal API.
Explore use cases for using npm vs. npx such as long-term dependency management or temporary tasks and running packages on the fly.
Validating and auditing AI-generated code reduces code errors and ensures that code is compliant.