How to improve the accessibility of your web apps

AccessibilityReact

Axe Chrome plugin

If you are working as a web developer you must have heard the term accessibility or a11y (opens in a new tab), and you have probably thought about it when writing HTML markup and developing JavaScript applications. However because of project constraints such as budget, time and scope it probably doesn’t have a high priority in the development process.

I would argue that this is a missed opportunity. Not only because in my opinion we have a moral obligation to make our products accessible to as many people as possible. But also because making accessibility part of your development workflow will increase the overall quality of your product and requires less effort than you think.

At CLEVER°FRANKE (opens in a new tab) we are building data driven web applications and tools for all sorts of organizations. Although we are by no means accessibility experts, we have incorporated accessibility as part of our daily workflow. In this article I will elaborate on the things we keep in mind during development and show that you can make big improvements with only minimal effort.

If you ask somebody why you should use heading or paragraph elements and alt attributes in your HTML markup they will probably say that it is good for search engine optimization (SEO). They are definitely right, but it is also equally important for the accessibility of your webpage. Using the proper semantic HTML tags and attributes makes sure assistive technologies such as screen readers can understand the way your webpage is structured. This helps keyboard users to quickly jump to specific sections on your page for example. Screen readers can jump through headings but also through so called landmarks such as <header>, <footer>, <section>, <article>, <aside> and <nav>. Wrong usage of these elements can cause screen reader users to accidentally miss important content. That's why it is paramount to use HTML elements in the way they are intended to be used.

You can find a list of all the HTML elements and their proper use cases at the wiki of W3C (opens in a new tab)

It can happen that for some reason you cannot use the proper HTML element because of browser incompatibility or styling issues. Or that you want to add some extra semantic information to a HTML element in order to, for example, let a user know that a dropdown is currently expanded. For those cases the W3C has created the WAI-ARIA (Accessible Rich Internet Applications) specification (opens in a new tab).

This specification provides an ontology of roles, states, and properties that define accessible user interface elements and can be used to improve the accessibility and interoperability of web content and applications. These semantics are designed to allow an author to properly convey user interface behaviors and structural information to assistive technologies in document-level markup.

So if you for some reason cannot use the <button> element you can add role="button" on a regular <div> element. And you can add aria-expanded="true" on a dropdown component to let the user know it is currently expanded. Please note however that WAI-ARIA is only adding semantic meaning to the elements. It does not change browser behavior, so you may need to mimic default element behavior by using JavaScript and manually adding tabindex attributes to make the elements focusable.

Trying to prevent and fix every possible accessibility issue can be quite overwhelming. Luckily there are (open-source) tools that can easily and automatically spot markup issues in your code for you.

Axe

At CLEVER°FRANKE we are using Axe (opens in a new tab) during development.

Axe is an accessibility testing engine for websites and other HTML-based user interfaces. It’s fast, secure, lightweight, and was built to seamlessly integrate with any existing test environment so you can automate accessibility testing alongside your regular functional testing.

Axe can be used via the command line as part of automated testing and you can also use it via their really helpful browser plugin (opens in a new tab). The browser plugin will analyze your webpage and shows you a list of issues that need to be fixed. You can see the impact of the issue and also why and how it should be fixed. This is really helpful if you want to quickly fix the majority of markup issues in your application.

Axe Chrome plugin

Axe Chrome plugin

We prefer to use Axe, but there are also other options available that may fit your needs better:

ESLint

Another great way to easily spot accessibility problems in your code is to use a linter.

A linter is a tool that analyses source code to flag programming errors, bugs, stylistic errors, and suspicious constructs.

Once installed it can notify you about code problems during development. Since we’re using ESLint (opens in a new tab) in our JavaScript projects, we make use of the easily configurable ESLint plugin eslint-plugin-jsx-a11y. This will notify us of accessibility problems directly in our editor.

Accessibility linter

Accessibility linter

Next to making sure that your HTML markup is correct it is also important to make sure users of assistive technology can make sense of images and data visualizations on your webpage. If you make use of informative images you should of course always make sure there is a descriptive alt attribute on the image tag.

Since we are a company that is specialized in working with data, we often include custom SVG data visualizations in our web applications. To make sure these are properly accessible we try to expose the story it needs to tell to keyboard users as well. The level of detail that should be exposed is dependent on the function of the data visualization. If you are developing a bar chart it is probably important to make the actual values accessible. In case you want to show a simple trend in the form of a sparkline (opens in a new tab), the actual values are less important and can be omitted.

<svg aria-labelledby="title" aria-describedby="desc" role="group">
  <title id="title">
    Histogram that shows the number of found journals per specific filter value.
  </title>
  <desc id="desc">
    This Histogram has a total of {xDomain.length} buckets. The maximum number
    of journals in a bucket is {maxValue}
  </desc>
  <g role="list" aria-label="histogram">
    {bucket.map((value, index) => (
      <Rect
        key={index}
        role="listitem"
        aria-label={`${value} journals in bucket ${index}`}
      />
    ))}
    {/* ... */}
  </g>
</svg>

For every data visualization we create a <title> and <desc> inside the SVG. The <desc> tag can include a dynamic description that is based on the actual data. In case actual values and elements of the data visualization need to be accessible as well, you can add semantic meaning to them by using the WAI-ARIA attributes we discussed earlier. Heather Migliorisi wrote a very elaborate article about how to create accessible SVGs (opens in a new tab), which should get you up to speed.

Accessible histogram component

Accessible histogram component on Elsevier ® JournalFinder (opens in a new tab)

Once you have implemented all the above, it is time to test your application to make sure everything is working as expected. At CLEVER°FRANKE we perform two types of testing: manual and automated testing.

Manual testing

The best way to check if your application is accessible by keyboard is to (you’ve probably already guessed it) actually use it with a keyboard 💡. To do so you will need to install a screen reader (opens in a new tab). We use the default screen reader that is part of MacOS called VoiceOver (opens in a new tab). You can enable it in the Accessibility panel in your System Preferences. Once enabled you can try to navigate your app with the tab key and check if all menu items and components are accessible by keyboard and are labelled properly. This is the best way to see if you forgot to add a tabindex somewhere, or to find out if you can actually select items in a dropdown without using your mouse and eyes. Once you are comfortable with the state of your app, it is time to make sure it stays accessible during future alterations.

Accessible dropdown component

Accessible dropdown component on Elsevier ® JournalFinder (opens in a new tab)

Automated testing

As part of our continuous integration pipeline we use Jest (opens in a new tab) for our unit and integration tests. Jest allows you to test the behavior and structure of your components, and throws an error if any modifications to your code break the functionality described in the test. Jest is also a great tool to test the accessibility of your components. To do so we use React Testing Library (opens in a new tab) (which is an extension of the DOM Testing Library (opens in a new tab)) on top of Jest.

React Testing Library encourages your applications to be more accessible and allows you to get your tests closer to using your components the way a user will, which allows your tests to give you more confidence that your application will work when a real user uses it.

This allows us to describe keyboard functionality in our tests and check if our components behave as expected. For example we can check if a Dropdown component has a proper label assigned to it and make sure it selects a specific value when pressing enter after navigating the dropdown items with the arrow keys.

test("the Dropdown is accessible by keyboard", () => {
  const labelText = "Select your shoe size";
 
  const { getByLabelText, rerender } = render(<Dropdown label={labelText} />);
 
  const dropdown = getByLabelText(labelText);
 
  fireEvent.keyDown(dropdown, { key: "ArrowDown" });
  fireEvent.keyDown(dropdown, { key: "ArrowDown" });
  fireEvent.keyDown(dropdown, { key: "Enter" });
 
  expect(/* ... */);
});

We can also test for accessibility problems in the DOM structure of our components by importing the Jest Axe library (opens in a new tab) in our Jest tests. This library will give the same results as the Axe Chrome plugin, only now fully automatic and during every deployment ⚡.

text("Dropdown does not throw a11y violations", async () => {
  const { container } = render(<Dropdown label={"Select your shoe size"} />);
  const results = await axe(container.innerHTML);
  expect(results).toHaveNoViolations();
});

Request an accessibility audit

After following all the steps above you should be pretty confident that your app is ready to be used by assistive technologies, but to be completely sure you can of course always request an accessibility audit by an expert.

To see an example of how we implemented the accessibility improvements mentioned above, you can visit the Elsevier ® JournalFinder (opens in a new tab).