Making React Apps Accessible It's easier than you think

#DDDAdelaide @jessbudd4

Jess Budd @jessbudd4 Front-end developer Accessibility consultant Senior digital producer Meetup organiser

🤕 😭 🚶📱 @jessbudd4

% = 💵 @jessbudd4

@jessbudd4

😬 @jessbudd4

Are JavaScript frameworks bad for accessibility? @jessbudd4

“ Nothing in React prevents us from building accessible web apps - Leslie Cohn-Wein, Netlify @jessbudd4

What is web accessibility? @jessbudd4

Removing barriers that prevent people with disabilities using your website @jessbudd4

1 5 in Australians experience some form of disability Source: www.and.org.au/pages/disability-statistics.html @jessbudd4

357,000 Australians have a visual impairment Source: www.and.org.au/pages/disability-statistics.html @jessbudd4

1 in 6 Australians are affected by hearing loss Source: www.and.org.au/pages/disability-statistics.html @jessbudd4

Vision Hearing Mobility Cognitive @jessbudd4

Credit: Undraw and Ben Usher Smith @jessbudd4

Accessibility benefits everyone @jessbudd4

Who is most impacted?

Keyboard users

Screenreade r users

Semantic HTML @jessbudd4

Why does it matter? @jessbudd4

screenshot of link list @jessbudd4

// not very accessible card component <div class=”product”> <div class=“name”> Product name </div> <div class=”description”> Description of product </div> <div class=”button”>Add to cart</div> </div>

// more accessible card component <li> <h2> Product name </h2> <p> Description of product </p> <button>Add to cart</button> </li>

Divs are not buttons @jessbudd4

<div tabindex=”0” role=”button” onKeyUp={keyUpHandler} onClick={clickHandler} class=”button”> Add to cart </div>

<div tabindex=”0” role=”button” onKeyUp={keyUpHandler} onClick={clickHandler} class=”button”> Add to cart </div>

<div tabindex=”0” role=”button” onKeyUp={keyUpHandler} onClick={clickHandler} class=”button”> Add to cart </div>

<div <button tabindex=”0” onClick={clickHandler}> role=”button” Add to cart onKeyUp={keyUpHandler} </button> onClick={clickHandler} class=”button”> Add to cart </div> 👍

Give buttons some ❤ @jessbudd4

Semantic markup is good for SEO @jessbudd4

Fragments for valid html @jessbudd4

// not allowed render() { return ( <li>Hello</li> <li>World</li> ); }

// not allowed // solution??? render() { render() { return ( return (

<li>Hello</li> <div> <li>World</li> <li>Hello</li> ); <li>World</li> } </div> ); } 👎

// fragments syntax render() { return ( <React.fragment> <li>Hello</li> <li>World</li> </React.fragment> ); }

Inputs & labels @jessbudd4

// label not linked to input <label> Dog breed: </label> <input type=”text” name=”breed” />

// explicitly linked label to input <label for=”uniqueId”> Dog breed: </label> <input id=”uniqueId” type=”text” name=”breed” />

// “for” becomes “htmlFor” in JSX <label htmlFor=”uniqueId”> Dog breed: </label> <input id=”uniqueId” type=”text” name=”breed” />

What if I can’t set a unique ID in advance? @jessbudd4

// implicitly linked label to input <label> Dog breed: <input type=”text” name=”breed”/> </label>

What if the design doesn’t have labels? @jessbudd4

// label hidden with css is still announced <label class=“sr-only“ for=“dogBreed”> Dog breed: </label> <input id=“dogBreed” type=”text” name=”breed” />

// visually hidden, but accessible to screen readers .sr-only { clip: rect(1px, 1px, 1px, 1px); clip-path: inset(5%); height: 1px; width: 1px; margin: -1px; overflow: hidden; padding: 0; white-space: nowrap; position: absolute; border: 0; }

Touch target @jessbudd4

Page titles @jessbudd4

Click to edit

// page title appears in browser tab <head> <meta charset=”utf-8”> <title>Dogs Are The Best</title> <link rel=”stylesheet” href=”style.css”> </head>

// code executes when component mounts componentDidMount() { document.title = ‘Heckin’ Good Doggo’; }

// update page title on routing componentDidMount() { document.title = ‘Heckin’ Good Doggo’; }

Update <head> elements React Helmet @jessbudd4

Click to edit

Visible focus styles @jessbudd4

Default browser styles Safari: Firefox: Chrome: @jessbudd4

Meme where am I @jessbudd4

Screen shot of page with masses of links and question marks ??? @jessbudd4

/* don’t just remove */ *:focus { outline: none; } ❌

/* don’t just remove */ :focus { outline: none; ❌ } / replace with something! */ :focus { / branded focus styles here */ border: 2px dotted currentColor; } ✅

/* extend hover styles */ .button:hover, .button:focus { border: 5px solid #33ffdb; }

Focus management @jessbudd4

Tabindex Explained // tabindex becomes tabIndex (camelCase) in JSX tabindex=”0” // makes element focusable in tab/DOM order tabindex=”-1” // makes elements focusable only via scripting tabindex=”5” // Danger Will Robinson!

React has silent routing @jessbudd4

Video of default browser behaviour @jessbudd4

Move keyboard focus on routing @jessbudd4

Use an accessible router Reach/Router @jessbudd4

Use React Refs @jessbudd4

class PageHeading extends React.Component { constructer(props) { super(props); this.content = React.createRef(); // Create Ref } componentDidMount() { this.content.focus(); // Move focus to ref } render() { return ( <h1 tabindex=”-1” ref={this.content}> // Reference Doggos, Puppers and Floofers </h1> ); } }

class PageHeading extends React.Component { constructer(props) { super(props); this.content = React.createRef(); // Create Ref } componentDidMount() { this.content.focus(); // Move focus to ref } render() { return ( <h1 tabindex=”-1” ref={this.content}> // Reference Doggos, Puppers and Floofers </h1> ); } }

class PageHeading extends React.Component { constructer(props) { super(props); this.content = React.createRef(); // Create Ref } componentDidMount() { this.content.focus(); // Move focus to ref } render() { return ( <h1 tabindex=”-1” ref={this.content}> // Reference Doggos, Puppers and Floofers </h1> ); } }

// update page title // and move users focus componentDidMount() { document.title = ‘Doggos be happy’; this.content.focus(); }

Modals have specific focus challenges @jessbudd4

Use an accessible modal react-modal @jessbudd4

Tooling @jessbudd4

eslint-plugin-jsx-a11y eslint-plugin-jsx-a11y @jessbudd4

Axe-core react-axe @jessbudd4

Accessibility Tree (Dev Tools) @jessbudd4

Google Lighthouse https://developers.google.com/web/tools/lighthouse @jessbudd4

Accessibility Insights https://accessibilityinsights.io/ @jessbudd4

https://www.matuzo.at/blog/building-the-most-inaccessible-site-possible-with-a-perfect-lighthouse-score/

“ Automated testing is just the first step - Manuel Matuzovic @jessbudd4

Testing @jessbudd4

Keyboard @jessbudd4

What to look for Can I see where focus is? Does the tab order make sense? Can I access all required elements? Can I use advanced components? Does my focus move when needed? @jessbudd4

Screenreader @jessbudd4

Voiceover (macOS) NVDA (Windows) JAWS (Windows) 💰 @jessbudd4

😎 @jessbudd4

@jessbudd4

Recommended Resources @jessbudd4

React Documentation https://reactjs.org/docs/accessibility.html @jessbudd4

Front End Masters Course https://frontendmasters.com/courses/javascript-accessibility/ @jessbudd4

Inclusive Components https://inclusive-components.design/ @jessbudd4

Scott Vinkle https://scottvinkle.me/ @jessbudd4

Takeaways Use Semantic HTML Link form labels with inputs Update page titles on routing Manage keyboard focus Use tooling & test your apps @jessbudd4

Go forth and make the web a better place @jessbudd4

Thanks! Slides at bit.ly/DDDA19 @jessbudd4