The jsx-a11y lint plugin is a great first line of defense to prevent accessibility issues, but no tool can cover everything. React-axe gives us a runtime check on our rendered DOM, and by adding it to our boilerplate, we’ll ensure that all our future projects have accessibility covered from multiple angles. In this lesson, we’ll install react-axe and add it to our entry point for development builds only.
Instructor: [00:01] Using the JSX accessibility plugin for ESLint helps us detect some accessibility violations through our JSX. We'll never be able to detect all accessibility issues without testing our app using assistive technology, but we can use multiple tools to catch things that our linter can't.
[00:17] There may be issues that aren't detectable until the app has finished rendering. We should add a way to detect issues in the browser.
[00:24] If we look at this markup, we'll see we have an h1 followed by an h2. If I change this h2 to an h3, I've broken the semantics of this markup by skipping a heading level. Let's run our linter and see if it catches that.
[00:42] As you can see, the linter passed. We have an accessibility issue on our markup, but the JSX accessibility plugin hasn't caught that. Let's add a way to detect this in the browser once the app has fully rendered.
[00:54] In the terminal, I'm going to install package as a dev dependency called react-x. With that installed, I'm going to go the index.js file. Up here after my import, I'm going to add an if statement. I only want to run this tool in development mode. This if statement is going to make sure that we don't do any of this in a production build.
[01:23] Here, I'm going to check process.env.node_env. This Node environment variable is going to be available through our build process based on the mode that we set in webpack. webpack is going to build with these values set to production for a production build and development for a dev build. I'm going to detect that this equals development.
[01:51] If we are in development mode, I'm going to create a const and call it X, and I'm going to set that to equal a call to require. I'm going to require react-x. I'm not even going to pull this package in unless we are in a development build. Then I'm going to run it.
[02:07] I'm going to call X. This is going to take references to our React and ReactDOM packages. I'm going to pass in React, ReactDOM. It's going to take a delay. This is going to give the application some time to render. We'll set that timeout to 1,000 milliseconds. We'll give it one second to render, and then it'll run this evaluation and look for accessibility violations in our rendered DOM.
[02:36] I can save that, and then I'm going to jump into the terminal and npm run dev to do a dev build of the application and run that in a browser.
[02:48] Our application is going to load up. If we open the dev tools, we're going to see in our console that we have new X issue. It's a moderate issue, heading level should only increase by 1. There is a link to some information on that if you want to read more about it.
[03:03] It'll actually show you the element. We can see here it's that h3 that has our count in it. It's this element right here.
[03:10] I'm going to go back to the code, and in app.js, I'll update this and I'll put it back to where it was, where we had an h2, save it. It'll recompile, and when the browser reloads, we'll see that it's an h2 and we don't get that error in the console anymore.
[03:32] I'm going to go back to the terminal and I'm going to stop the dev server. I'm going to do a production build. npm run build. Now I'm going to open the bundle-analyzer output. I'll open dist/bundle-sizes.
[03:55] We can verify that when we look at this production build output, that react-x has not been included in this production build. Now we have a reliable way to detect accessibility violations in our rendered DOM only in development mode.
I got another error when I loaded the Axe checker in dev mode. It complained about "Document must have one main landmark" and other such issues. To fix it just put a <main> tag around the content in App.js (I put mine directly underneath the top level <div>.