Creating Accessible React Apps

Can React apps be accessible? 🤔

Yes! 🎉

Children from Charlie Brown cartoon celebrating as baloons fall from the ceiling.

Continue on to find out how…

Agenda

  1. Notes on getting started
  2. Setting the page title
  3. Live announcements
  4. Focus management
  5. React's accessibility linter
  6. Using React.Fragment
  7. Writing semantic HTML
  8. Demo!

Notes on getting started 📓

Ryan Howard from The Office, looking rather annoyed, writes in a notebook.

camelCase

Write attributes in camelCase 🐪

  • tabindex 👉 tabIndex
  • contenteditable 👉 contentEditable
  • maxlength 👉 maxLength

(aria-* and data-* are exempt from this.)

Reserved words

When HTML conflicts with JS 😕

  • for 👉 htmlFor
  • class 👉 className

Self closing elements require /

  • <img src="…" alt=""> 👉 <img src="…" alt="" />
  • <meta charset="utf-8"> 👉 <meta charset="utf-8" />
  • <input type="text"> 👉 <input type="text" />

Setting the page title 📰

Homer Simpsons's web page, featuring many animated gifs and a dancing Jesus.

The problem… 🤔

When someone using a screen reader loads a new page, the initial page announcement is always the same.

Set the page title! 👍

  • Updates the browser tab for sighted users
  • Helps with Search Engine Optimization
  • Often the first content announced by screen readers

Use document.title

            
              componentDidMount() {
                document.title = 'My page title';
              }
            
          

Other existing components

  • react-document-title
                    
                      <DocumentTitle title='My page title'>
                        <!-- content -->
                      </DocumentTitle>
                    
                  
  • react-helmet
                    
                      <Helmet>
                        <meta charSet='utf-8' />
                        <title>My page title</title>
                        <link rel='stylesheet' href='css/page-specific.css' />
                      </Helmet>
                    
                  

Live announcements 📢

A man using a megaphone. For some reason, a hamburger is seen coming from his mouth, going through the megaphone, and out comes an even larger hamburger.

The problem… 🤔

When a Single Page App fetches data, the new content is visibly displayed.

However, screen reader users recieve no indication of the available content.

Create a Live Announcements component 👍

            
                import React from 'react';

                class Announcements extends React.Component {
                  render() {
                    return (
                      <div aria-live="polite" aria-atomic="true" className="visuallyhidden">
                        {this.props.message}
                      </div>
                    );
                  }
                }

                export default Announcements;
            
          

Usage, part 1

Create a state property

            
              this.state = {
                message: null
              };
            
          

Usage, part 2

Set the state property

            
              this.setState({
                message: 'Some message about the current state'
              });
            
          

Usage, part 3

Send message to <Announcements /> component

            
              <Announcements message={this.state.message} />
            
          

Other existing components

  • react-aria-live
                    
                        <LiveAnnouncer>
                          <LiveMessage message={this.state.a11yMessage} aria-live="polite" />
                          <button onClick={() => {this.setState({ a11yMessage: 'Button Pressed' });}}>
                            Press me
                          </button>
                        </LiveAnnouncer>
                    
                  
  • react-a11y-announcer
    (Literally the same thing I described earlier)

Focus management ☕️

Bill Lumber, from Office Space, standing, arm resting on an office cubicle wall, slowley sipping coffee.

The problem… 🤔

A loading screen in our app displays a message in-between fetching new data. Sighted users can read the message a wait patiently… probably.

However, screen reader users do not recieve this message conveying the current app state. They might get discouraged or blame themselves when something takes a long time to load.

Focus management! 👍

Let's shift focus to the Loading message container when it gets displayed.

Selecting an element

Kind of like jQuery. 🤷‍♂️

            
              var loadingMessage = $('#loadingMessage');
              // …
              loadingMessage.focus();
            
          

The React way

Use a function ref to reference the element elsewhere in the component.

            
              <div
                ref={
                  (loadingMessage) => {
                    this.loadingMessage = loadingMessage;
                  }
                }
                tabIndex='-1'
                >
                Loading…
              </div>
            
          

Using the ref

            
              componentDidMount() {
                this.loadingMessage.focus();
              }
            
          

More focus management! 🐶

A pair of adorable pug puppies running on green grass.

The problem… 🤔

When using React Router's <Link /> component, the browser never actually reloads, leaving the user's focus state in an unknown position.

Our new friend, ref

            
              <div
                ref={
                  (contentContainer) => {
                    this.contentContainer = contentContainer;
                  }
                }
                tabIndex="-1"
                aria-labelledby="pageHeading"
                >
                  <Header />
                    <h1 id="pageHeading">…</h1>
                    // Content components…
                  <Footer />
              </div>
            
          

Set "page" focus via ref

            
              componentDidMount() {
                this.contentContainer.focus();
              }
            
          

🔥 New ref syntax

React 16.3 will be introducing a new, simpler way to create refs.

The createRef() method

            
              // 1. Create a new class property
              loading = React.createRef();

              // 2. Assign the class property to
              // the element via ref prop
              <div ref={this.loading} tabIndex="-1">
                Loading…
              </div>

              // 3. Call the focus method within componentDidMount
              this.loading.value.focus();
            
          

Using Fragment components 🍰

A close-up of a cupcake. Its top icing decoration is slowley being applied, one icing bag squeeze at a time with pink and blue icing.

What are Fragments?

Fragments let you group a list of elements/child components without adding extra nodes to the DOM.

Wrapping your content with a div

            
              render() {
                return (
                  <div>
                    <td>Hello</td>
                    <td>World</td>
                  </div>
                );
              }
            
          

…may lead to invalid HTML 😱

            
              <table>
                <tr>
                  <div>
                    <td>Hello</td>
                    <td>World</td>
                  </div>
                </tr>
              </table>
            
          

Our new friend React.Fragment

            
              render() {
                return (
                  <React.Fragment>
                    <td>Hello</td>
                    <td>World</td>
                  </React.Fragment>
                );
              }
            
          

The result 👍

            
              <table>
                <tr>
                  <td>Hello</td>
                  <td>World</td>
                </tr>
              </table>
            
          

React's accessibility linter 🛀

A smiling man opens a door to greet a guest. While doing so the man uses a lint roller to remove lint from his neck tie.

eslint-plugin-jsx-a11y

Outputs errors to the browser console automatically.

            
              <img src="images/chrome-console.png" />
            
          
Screen capture of Chrome's developer tools console. A warning message states, 'img elements must have an alt prop, either with meaningful text, or an empty string for decorative images.'

Code editor a11y linting? 🤔

Let's output errors directly in the editor, too.

Screen capture of Atom text editor. A warning message appears overtop of some code with the following message, 'img elements must have an alt prop, either with meaningful text, or an empty string for decorative images.'

1. Install ESLint Plugin

2. Install ESLint && eslint-plugin-jsx-a11y

            
              npm install eslint eslint-plugin-jsx-a11y --save-dev
            
          

3. Update the .eslintrc file

Add to the "plugins" section:

            
              "plugins": [
                "jsx-a11y"
              ]
            
          

Add to the "extends" section:

            
              "extends": [
                "plugin:jsx-a11y/recommended"
              ]
            
          

Writing semantic HTML in React ⌨️

Kermit the Frog aggressively types on an old type writer.
Screen capture of Twitter. A tweet from Heydon Pickering (@heydonworks) reads, 'React code doesn't have to be inaccessible any more than hip-hop has to be misogynist.' Opens in a new window.

React Components are ES6 Classes

Since React uses ES6 classes and standard HTML markup to generate its components, it’s up to you to continue to write good, clean, semantic HTML.

In other words…

If there are accessibility issues, it's your fault . This is a good thing! 👍

Demo! 📺

Check out the demo app, TV-Db 👉.

Demo Highlights 📱

  1. The page title being updated
  2. The message, "loading" was announced
  3. Number of items avaialble was announced
  4. Keyboard focus started from the top of the page
  5. There was a visible blue outline on each focusable element

Bonus! 💥

Screen capture of Twitter. A tweet from Ryan Florence (@ryanflorence) reads, 'Announcing Reach Router: An Accessible Router for React.' Opens in a new window.

Thanks! 🙂