Author avatar

Gaurav Singhal

How to Create Ordered Lists with HTML5 for Screen Readers

Gaurav Singhal

  • Jul 17, 2019
  • 8 Min read
  • 226 Views
  • Jul 17, 2019
  • 8 Min read
  • 226 Views
Web Development
React

Introduction

Web accessibility is an important aspect to assure everyone can access the web impartially and on the same level. This means a website must also be usable and accessible by people with disabilities. When building React applications, it can be easy to forget about the accessibility of the application we are building. In this guide, we are going to converge on making the web favorable for people who are visually impaired.

Screen Readers

First, let’s explore a brief explanation of screen readers so that we can recognize the needs of our users.

"Screen readers take the semantic information from your elements and produce an alternative, spoken UI, for a user with visual impairments."

By default, HTML5 lists have the right semantics for screen readers to announce them as lists. But while building web applications, due to one or more reasons, we might not be able to use the native HTML5 lists. Well, that's where ARIA roles come to the rescue.

Accessibility Rich Internet Applications (ARIA)

ARIA stands for Accessibility Rich Internet Applications; it's a specification that contains a set of attributes that you can add to an element. It can manipulate the accessibility tree and add in additional semantics. An accessibility tree is simply a subset of the DOM tree. The browser removes all the presentation elements and modifies the DOM tree so that it can be suitable for assistive technologies like screen readers. This remodeled DOM tree is called as the accessibility tree.

It is best to use the native HTML elements to get the right semantics, but sometimes when working on third-party code, or while developing complex UI, you might have to build custom elements. We can use ARIA attributes to give these custom elements the right semantics so that they can be announced rightly by screen readers.

Some of the information that the screen reader announces about an ordered or unordered list include:

  • The size of the list
  • The position of each item in the list
  • When the user has left one list and has entered another

While it depends on the individual screen reader, the above points are the most common.

Example (Finally! Some Code)

Let's begin with how we can use ARIA attributes to make our document accessible.

An HTML element can have a role attribute of list or listitem, so that it can be interpreted as a list by screen readers. Ordered or unordered list, it's all the same for ARIA. When traversing native list elements, the screen readers announce the number before the start of an ordered list item and "bullet" before the start of an unordered list item but it never announces the list as ordered or unordered; it only gets becomes while traversing the list.

1
2
3
4
5
<div role="list">
    <div role="listitem">HTML</div>
    <div role="listitem">CSS</div>
    <div role="listitem">JavsScript</div>
</div>
html

In the above snippet, div tags are used to be represented as lists and list items. The screen readers will interpret and announce it as "List with three items" and at the end of each list item, it will announce "listitem". But this isn't much help to make the user understand whether it's an ordered or unordered list.

To announce a number before each list element, we can use the aria-label attribute.

1
2
3
4
5
<div role="list">
    <div role="listitem"><span aria-label="1"></span> HTML</div>
    <div role="listitem"><span aria-label="2"></span> CSS</div>
    <div role="listitem"><span aria-label="3"></span> JavaScript</div>
</div>
html

I'm using a ChromeVox screen reader for the demo; there are many screen readers out there, some of which will be linked to at the end of this article.

It can be tempting to use role on every element but, as I mentioned earlier, it's highly recommended to use the native elements that have the right semantics to build a list (because with great powers come significant responsibilities). Having said that, the role should not be used on native elements since they have implicit semantics. For example, ol or ul tags need not have a role=list attribute, and likewise, li tags need not have role=listitem.

Accessible Lists in React

But how would ARIA attributes fit in with React Application, more precisely in JSX?

JSX compiles down to HTML, and it is possible due to ineffective methods JSX may compile to non-semantic HTML and we all know how difficult it is to interpret for screen readers. This ends up producing inaccessible web apps.

For example:

1
2
3
4
5
6
7
8
render() {
    return (
        <div>
            {this.state.frameworks.map(f => <li>{f}</li>)}
            <li> Ember </li>
        </div>
    )
}
jsx

Writing such JSX will result in non-semantic HTML. The above code, when compiled, will result in the following:

1
2
3
4
5
6
7
<ol>
    ...
    <div>
        <li>...</li>
        <li> Ember </li>
    </div>
</ol>
html

Screen readers wouldn't announce the list in this case and will only list individual list items, which can be confusing to the end-user.

The correct way to wrap a component's children is by using React Fragments.

React Fragment

"A common pattern in React is for a component to return multiple elements. Fragments let you group a list of children without adding extra nodes to the DOM."

Using the <React.Fragment> component, we can add a parent element to JSX elements without introducing an extra node in the DOM (Document Object Model), which means the <React.Fragment> outputs no HTML Element.

There is also a shorthand version of React Fragment - <>...</>

Complete Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import React, { Component } from "react";
import ReactDOM from "react-dom";

class JSFrameworks extends Component {
  constructor(props) {
    super(props);
    this.state = {
      frameworks: ['React', 'Angular', 'Vue']
    }
  }

  render() {
    return (
      <>
        {this.state.frameworks.map(f => <li>{f}</li>)}
        <li>Ember</li>
      </>
    )
  }
}

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      listItems: ['HTML', 'CSS', 'JavaScript']
    };
  }

  render() {
    return (
      <div className="App">
        <h1> Inaccessible Lists </h1>
        <ol>
          {this.state.listItems.map(item => <li>{item}</li>)}
          <ol type='i'>
            <JSFrameworks />
          </ol>
        </ol>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
jsx

ARIA in React Apps

React fully supports the use of ARIA-* attributes in JSX. We don't need to camel-case the attribute names in case of the ARIA(even React is afraid to mess with ARIA - sorry no more Arya jokes I promise).

1
2
3
4
5
6
7
8
9
render() {
    return (
        <div role="list">
            <div role="listitem"><span aria-label="1"></span> HTML</div>
            <div role="listitem"><span aria-label="2"></span> CSS</div>
            <div role="listitem"><span aria-label="3"></span> JavaScript</div>
        </div>
    );
}
jsx

Conclusion

I hope you are now convinced of how important it is to build accessible web apps and can take some notes from this guide. Now, get out of here and start building stuff. Cheers and Happy Coding.

Popular Screen Readers

1