Making React Apps Accessible: It’s easier than you think

A presentation at DDD Adelaide in November 2019 in Adelaide SA, Australia by Jess Budd

Slide 1

Slide 1

Making React Apps Accessible It's easier than you think

#DDDAdelaide @jessbudd4

Slide 2

Slide 2

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

Slide 3

Slide 3

🤕 😭 🚶📱 @jessbudd4

Slide 4

Slide 4

% = 💵 @jessbudd4

Slide 5

Slide 5

@jessbudd4

Slide 6

Slide 6

😬 @jessbudd4

Slide 7

Slide 7

Are JavaScript frameworks bad for accessibility? @jessbudd4

Slide 8

Slide 8

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

Slide 9

Slide 9

What is web accessibility? @jessbudd4

Slide 10

Slide 10

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

Slide 11

Slide 11

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

Slide 12

Slide 12

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

Slide 13

Slide 13

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

Slide 14

Slide 14

Vision Hearing Mobility Cognitive @jessbudd4

Slide 15

Slide 15

Credit: Undraw and Ben Usher Smith @jessbudd4

Slide 16

Slide 16

Accessibility benefits everyone @jessbudd4

Slide 17

Slide 17

Who is most impacted?

Slide 18

Slide 18

Keyboard users

Slide 19

Slide 19

Screenreade r users

Slide 20

Slide 20

Semantic HTML @jessbudd4

Slide 21

Slide 21

Why does it matter? @jessbudd4

Slide 22

Slide 22

screenshot of link list @jessbudd4

Slide 23

Slide 23

// 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>

Slide 24

Slide 24

Slide 25

Slide 25

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

Slide 26

Slide 26

Divs are not buttons @jessbudd4

Slide 27

Slide 27

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

Slide 28

Slide 28

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

Slide 29

Slide 29

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

Slide 30

Slide 30

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

Slide 31

Slide 31

Give buttons some ❤ @jessbudd4

Slide 32

Slide 32

Semantic markup is good for SEO @jessbudd4

Slide 33

Slide 33

Fragments for valid html @jessbudd4

Slide 34

Slide 34

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

Slide 35

Slide 35

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

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

Slide 36

Slide 36

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

Slide 37

Slide 37

Inputs & labels @jessbudd4

Slide 38

Slide 38

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

Slide 39

Slide 39

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

Slide 40

Slide 40

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

Slide 41

Slide 41

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

Slide 42

Slide 42

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

Slide 43

Slide 43

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

Slide 44

Slide 44

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

Slide 45

Slide 45

// 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; }

Slide 46

Slide 46

Touch target @jessbudd4

Slide 47

Slide 47

Page titles @jessbudd4

Slide 48

Slide 48

Click to edit

Slide 49

Slide 49

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

Slide 50

Slide 50

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

Slide 51

Slide 51

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

Slide 52

Slide 52

Update <head> elements React Helmet @jessbudd4

Slide 53

Slide 53

Click to edit

Slide 54

Slide 54

Visible focus styles @jessbudd4

Slide 55

Slide 55

Default browser styles Safari: Firefox: Chrome: @jessbudd4

Slide 56

Slide 56

Meme where am I @jessbudd4

Slide 57

Slide 57

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

Slide 58

Slide 58

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

Slide 59

Slide 59

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

Slide 60

Slide 60

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

Slide 61

Slide 61

Focus management @jessbudd4

Slide 62

Slide 62

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!

Slide 63

Slide 63

React has silent routing @jessbudd4

Slide 64

Slide 64

Video of default browser behaviour @jessbudd4

Slide 65

Slide 65

Move keyboard focus on routing @jessbudd4

Slide 66

Slide 66

Use an accessible router Reach/Router @jessbudd4

Slide 67

Slide 67

Use React Refs @jessbudd4

Slide 68

Slide 68

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> ); } }

Slide 69

Slide 69

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> ); } }

Slide 70

Slide 70

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> ); } }

Slide 71

Slide 71

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

Slide 72

Slide 72

Modals have specific focus challenges @jessbudd4

Slide 73

Slide 73

Use an accessible modal react-modal @jessbudd4

Slide 74

Slide 74

Tooling @jessbudd4

Slide 75

Slide 75

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

Slide 76

Slide 76

Axe-core react-axe @jessbudd4

Slide 77

Slide 77

Accessibility Tree (Dev Tools) @jessbudd4

Slide 78

Slide 78

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

Slide 79

Slide 79

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

Slide 80

Slide 80

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

Slide 81

Slide 81

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

Slide 82

Slide 82

Testing @jessbudd4

Slide 83

Slide 83

Keyboard @jessbudd4

Slide 84

Slide 84

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

Slide 85

Slide 85

Screenreader @jessbudd4

Slide 86

Slide 86

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

Slide 87

Slide 87

😎 @jessbudd4

Slide 88

Slide 88

@jessbudd4

Slide 89

Slide 89

Recommended Resources @jessbudd4

Slide 90

Slide 90

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

Slide 91

Slide 91

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

Slide 92

Slide 92

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

Slide 93

Slide 93

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

Slide 94

Slide 94

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

Slide 95

Slide 95

Go forth and make the web a better place @jessbudd4

Slide 96

Slide 96

Thanks! Slides at bit.ly/DDDA19 @jessbudd4