Accessibility in JavaScript Applications

William Harrison
12 min readSep 4, 2019

--

As stolen from the Frontend Masters course of the same name

Make the web more inclusive with and for people with disabilities

Learn more about inclusivity:
https://bit.ly/microsoft-inclusive-toolkit

Overview

  • Accessibility Debugging
  • Accessibility in JavaScript Apps
    ◦ Focus Management
    ◦ Announcements (for Screen Readers)
    ◦ Semantic HTML
    ◦ Unobtrusive Motion (animations)
    ◦ Progressive Enhancement
  • Accessibility Units (testing)
  • Accessible Pages

Do this if you want to follow along…you SHOULD follow along. This workshop is using Gatsby & React.

git clone https://github.com/marcysutton/js-a11y-workshop.git
cd js-a11y-workshop/
npm install
npx gatsby develop

if you are following above, open: http://localhost:8000/
if you would like to test on a production build, run: gatsby build && gatsby serve then visit: http://localhost:9000gatsby build && gatsby serveneeds to be ran again when changes are made

Some Accessibility Tools

NVDA (Windows)
axe extensions
Accessibility Insights
There are additional tools and resources available in the project files

A11y debugging

  • How does it render in a web browser
  • Test the controls with the keyboard
    ◦ buttons, links, inputs, text areas
    ◦ are elements focusable, are there hrefs on links, is there an outline on selectable elements
  • Use accessibility web extensions
    axe, accessibility insights
  • Check color contrast
    ^ top problem with accessibility ^… according to Marcy 🙄
  • Test with screen readers
  • Use magnification & zoom 🔎
    make sure you aren’t breaking layouts

Hidden vs. Visible CSS

Screen reader friendly CSS. Does not render, but is visible by screen readers.

.visually-hidden {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}

Reserve the elements space in the layout, maybe for animation purposes. Visible by screen readers.

.opacity {
opacity: 0;
}

Show this element to NO ONE!

.displayNone {
display: none;
}

Reserve the elements space in the layout, but remove the visibility by a screen reader?

.visibility {
visibility: none;
}

The Accessibility Tree (of life)

chrome://accessibility ← cannot make url 🥴

Anyway, use the Accessibility Inspector, the (non)link shows you minified code that is not enjoyable to look at.

Testing Active Element Focus in Developer Console

Paste the code below to log focused / active element to console

document.body.addEventListener('focusin', (event) => {
console.log(document.activeElement)
})

If you are trying to test accessibility in Safari, sorry for you, but go to Settings and turn on “Press Tab to highlight each item on a webpage”… without it, you won’t see any highlights.

At this point in the course, it was recommended to test out ANY website with ANY one of the tools found at this link

Color Contrast Testing with Chrome Developer Tools

Chromes color picker will show contrast ratios for elements. The instructor suggested slightly modifying the design teams colors (if needed), but not telling them (if you can get away with it and it is not a brand color).

Screen Reader Cheat Sheets

Find them here

Screen Magnification Testing

Zoom in… use relative font sizes over explicit font sizes. Nothing broken at 200% is the standard according to the Web Accessibility Guidelines

The Things You Are Actually Here to Learn About

Focus Management

Moving the user’s focus as part of an interaction to alert them to new content

Focus Management Building Blocks

  • Reachable and operable elements
  • TAB, escape, and arrow keys
  • Visible focus styles
  • Hidden / Inert content

tabindex in HTML

Make non-interactive elements focusable

tabIndex="0"       // in the tab order
tabIndex="-1" // focusable by script, or removes from tab order
tabIndex="99641" // all up in your tab order, hard to manage

Any positive tabindex values will take the element out of the natural order of tabs.

tabindex + role + name

Expose accessibility information for focusable elements

<div tabIndex="0" role="button" aria-label="Close"></div>
// a focusable button widget (not a DIV) with an accessible name

tabindex + role + name + events

Make custom controls fully interactive

<div tabIndex="0" role="button" aria-label="Close" onClick={clickHandler} onKeyDown={keydownHandler}></div>
// 5 properties to make a DIV an accessible button?
// use a button?
<button aria-label="Close" onClick={clickHandler}></button>

ARIA: Accessible Rich Internet Applications

  • role: what is it? i.e. what type of element is it
  • state: what’s happening to it? i.e. is it selected
  • property: what’s the nature of it? i.e. aria-label

The first rule of ARIA is DON’T USE IT

…basically, try to avoid using non-semantic HTML

Modal Layers: disabling background content

  • aria-hidden=”true” tabindex=”-1"
  • inert(+ polyfill) ← a proposed element
  • CSS display: none ← NO ONE can 👀 it

It is important to manage focus in the following situations, otherwise your user could encounter some unintended side-effects: dropdowns and menus, layers and modals, view changes and deletes, loading screens.

Use a link for navigation, use a button for an action:

<a href="/page">I will take you places!</a><button onClick={action}>Click me and I will do a thing!</button>

Visible focus styles

For accessibility purposes, don’t:

*:focus {
outline: none;
}

Instead:

:hover, :focus {
outline: 5px auto blue;
}

ALSO, style input modalities

There is a (partially-supported) property, :focus-visible that provides a different focus indicator based on the user’s input modality (mouse vs. keyboard). If it is absolutely necessary to provide alternate focus styling according to a user’s input modality, what-input (A global utility for tracking the current input method (mouse, keyboard, or touch), as well as the current intent (mouse, keyboard, or touch).) could be used. It is far easier to just give all users the same focus styling, and to be accessible by not disabling focus.

CSS-in-JS

JavaScript generated classnames are not accessible. If you have to do it, don’t.

Accessible Dropdown Exercise

Compare the project files bad dropdown & better dropdown to see the differences between what makes a dropdown ‘bad’ from an accessibility standpoint and what can be done to make it ‘better’.

The code has been commented with key takeaways from the course.

Here is a Vanilla JS solution to an accessible dropdown

Routing & Skip Links

The recommended approach to routing is as follows:

  • Small UI control in each view target, like a skip link
  • Label with nearby content and its action, i.e. “Portfolio, skip to navigation”
  • When user clicks nav link, move focus to this control

The purpose of the above is to cover numerous usability issues, primarily screen readers and users who rely on tabbed navigation.

Underlying tech of Focus Management patterns

Coding a Skip Link

This course is using Gatsby.js. Vanilla JS links provided when possible.

Checkout the gatsby-browser.js file (provides API handles to what Gatsby does on the client side). We’re looking at the onRouteUpdate API handle. Immediately, for the skip link, we need to see if prevflocation exists, so add:

In this file there is a skip link on lines 7–9, this code may change. Just check it out for now.

Now have a look at the route-target-heading.js file. It is already complete, but has comments added to make it less (or more?) readable.

And now we get to wire it up!

Initially, changes are made to the animation.jsx page, then copied to all of the other pages (replacing all of the <h2>s). Also, need to declare the RouteTargetHeading import for all the pages. With all that set, we shift back to the gatsby-browser.js file, which has more commented code from the demo. The Marcy Sutton chose to make the skip link shift back to the sidebar navigation, but mentions that with user testing it may make sense to shift focus elsewhere. If you are running the application locally, use the dev tools and check out the Accessibility tab to see how the skip link is labeled in an accessible way.

Here is the Vanilla JS codepen to work on handling skip links

Here is the Vanilla JS solution to handling skip links

Announcements using ARIA Live Regions

Announcement patterns to notify assistive tech users without moving focus

  • Asynchronous save / update / etc.
    Don’t shift focus when saving or updating, but announce that it has been done
  • Combobox usage / list filtering
  • Chat widgets
  • Title changes*

ARIA Live Regions

Message command centers of varying importance

<div role="status" aria-live="polite">Polite will wait until everything is done announcing before it says something</div>
<div role="alert" aria-live="assertive">Assertive will immediately alert aka 'rude'</div>

The above ARIA Live regions are something that would be there at page load, then appended to while a user is interacting with your app.

If you are running the project locally, go to http://localhost:8000/slides/53 (alternatively, you can see the slide deck here: https://marcysutton.github.io/js-a11y-workshop/slides/53) and turn on the screen reader (on a Mac option+⌘+F5 will open system preferences for accessibility). Here you can see an example of an accessible search. It’s a 🔥 example of ARIA Live in action.

Live Region Tips

  • Include multiple regions for stubborn situations
  • Politeness levels depend on the use case
  • Site-level announcement manager APIs check this out

Live Region Demo

Have a look at http://localhost:8000/announcer, (or, https://marcysutton.github.io/js-a11y-workshop/announcer) this is where the more accessible component will live, check out the commented code here.

Here is the Vanilla JS solution to handling live regions

Progressive Enhancement

Semantic HTML

  • Use headings & landmarks
    h1 — h6, html, main, nav, header
  • Start with native UI controls
  • Build semantics into templates
  • Verify assistive tech output
    Test your things

Who cares (about progressive enhancement)?

Headings save time and programmatic information is useful. Semantic HTML communicates what’s on a page to users of assistive tech, reader modes, conversational UIs, search engines, and more. Apparently there is this thing called The Mozilla Developer Network that has all the details about all the things.

Tools for testing semantics

Add aria-label(s) to differentiate your elements, checkout the layout demo at: http://localhost:8000/layout (or, https://marcysutton.github.io/js-a11y-workshop/layout) for some examples of how to use semantic HTML. Style things however you want (unless you are removing outlines for :focus or :hover), but use semantic HTML. Elements such as header or nav automatically get the role of what they are, i.e. header role=[header].

Unobtrusive motion

Build safe and delightful interfaces! 😃 Hey UX team! Those awesome interactive things you are making may make our users uncomfortable.

  • prefers-reduced-motion
    A CSS media query that can change how our animations display depending on an operating system level preference (supported in all browsers, except for… IE & Edge because they hate disabled people)
  • media, animation playback controls
    Give the user more control for how they interact with your fancy things
  • opt-out for autoplay
    Let them choose!

⚠️ This animation demo may make you feel uncomfortable ⚠️

read over the prefers-reduced-motion doc on MDN

If you want to be nice to people who prefer reduced motion, that could look like this:

@media (prefers-reduced-motion: reduce) {
.what-is-your-class-name {
transition: none;
}
}
Check out the animation above and turn the Reduce Motion setting on or off

Progressive enhancement

Emphasize core web page content first, then add layers of presentation and features on top as browsers / network connections allow

  • Turn off JavaScript
    People may turn off JavaScript for network or data reasons, accessibility reasons, i.e. to get away from animation
  • Provide accessible baseline markup
  • Add ARIA w/ scripting
  • Prioritize core user flows
    Who uses the site most? Cater to them

Coding a Progressive Enhancement Component

Check out the page Enhanced Tab List page here

The finished component is here. In the terminal, run:

gatsby cleangatsby build && gatsby serve

The above commands will build and serve the workshop site. If you can figure out how to disable JavaScript, checkout the markup of the Enhanced Tab List page with both JavaScript turned on and off. With JavaScript turned on, you will see the ARIA role on the <li>s, with JavaScript turned off, you should just see <li>s without any additional role. If you are following along, run this in terminal:

gatsby cleangatsby develop

Here is a Vanilla JS example of progressive enhancement for the tab switcher component from the above links. Apparently it may not be completely finished, but some of the ideas of progressive enhancement are there…

Testing

Accessibility Testing Overview

Improve quality in development. Testing for accessibility specifically, you can bake-in accessibility patterns to check for.

  • Linting
    Testing for quality live in a file or on a commit
    eslint-plugin-jsx-a11y ← obv. only for JSX, but if you are following this tutorial and therefore working with GatsbyJS 👍… otherwise, there should be a linter that is suitable for whatever tech stack you are using
  • Unit Tests
    Mostly component-level API concerns, follow this Unit Test Approach:
    → Test code in isolation — tests without outside input
    → Stub inputs / fixture data — limit dependencies and pass-in inputs
    → Often headless — not technically rendered in a browser, i.e. JS DOM
    → Fast changing of state — check focus, labeling, etc…
  • Integration / End-to-end
  • Accessibility Tests APIs
  • Continuous Integration
  • Manual & User Testing

Accessibility Unit Tests

  • Component-specific Behavior
  • Interaction / Focus APIs ← make sure focus is sent to the right elements
  • Text Alternatives ← make sure the props you are passing into custom elements are going to the right place
  • ARIA States ← toggle ARIA states to test that they are working

Using a Testing Library

Working in a React-based application, the testing library used for this is Jest. Here is another link to checkout some testing information, and Jest DOM is a useful add-on to Jest to test the state of the DOM.

If you are working with the project repo, you are a lucky duck because in the package.json file the testing libraries are already preconfigured to be installed for you!

We’ll be looking at a couple of different files for testing, dropdown.test.js and dropdown.js, check them out to see how tests are written for Jest / JSX. Most of the notes are in the dropdown.test.js file.

The importance of testing should not be overlooked, especially for accessibility. If you verify your code via tests, you can rest assured (if you pass your own tests) that everything will work as expected.

Unit Testing Exercise

There is one… but it’s basically what you just read above. Maybe you could come up with some more things to test and… test them?

Accessible Pages — The Interoperability of Components

i.e. handling focus when switching between different components, like the client side routing that was handled earlier, you could test that.

Integration & End-to-end Tests

Are they synonymous?

  • Real-world Browser Testing ← i.e. using Chrome, Safari, Firefox, IE to test
  • Document / Page-level Rules ← Does your fully rendered HTML page have all the accessible bits? Page titles and such
  • Widget Interrop ← as noted above, how do components work together
  • Color Contrast ← test it
  • Framework Flexibility ← Don’t necessarily rely on the built-in testing of frameworks, it can be wonky

No matter what kind of test: focus on the outcome, not the code implementation! 🌟

The idea being, that you should be able to switch out your entire implementation and the tests should still pass.

Don’t bother writing accessibility test rules; THEY EXIST.

Use an A11y Test API, don’t write the rules

Leave it to the test people, they stay aware of all the changes to different languages so that their APIs function well and are generally up-to-date

Automation and Accessibility?

Tests can catch roughly 30–50% of accessibility issues, depending on the rule set. Screen readers cannot be automated. 😦

Write a combination of tests to catch a variety of bugs 💻 🔎 🐛

Writing Integration Tests (in Cypress)

Read the Cypress docs and have a look at the assertion test that was set up in the repo.

Continuous Integration

Opportunities to test for accessibility on EVERY commit, pull request, and deployment https://marcysutton.github.io/a11y-and-ci/

Automated testing and linting only gets us so far. We need manual human testing, too… Read more about that

Wrapping Up

Working directly with people who have disabilities is the absolute best way to test whether your site or app is truly accessible. There are groups that you can engage to help you with accessibility testing done by real humans! Here are a couple of those groups to check out:

At least with Fable (Tech Labs), you can set up a 30 minute session with a person who has a disability and they will walk through your site or app and provide you with feedback.

--

--