Running axe-core in the browser console

Published: | Author: Russ Weakley

What this tutorial is about

This tutorial will help you understand how axe-core works by running it directly in the browser console. You'll see how axe-core produces accessibility test results and how tools like axe DevTools build on this functionality to make testing easier.

Tools like browser extensions can seem a bit magical. But under the surface, axe-core just produces a set of results. How you look at those results, filter them, report on them and act on them is up to you.

About axe-core

axe-core is a free, open-source accessibility testing engine. It's the engine inside tools like the axe DevTools browser extension.

In this tutorial, you'll run axe-core directly from Chrome's browser console. There's nothing to install, and no extension needed.

The basic idea behind axe tools:

  1. Load a page.
  2. Run an accessibility test.
  3. Do something with the results.

Every axe tool, from browser extensions to automated test suites, follows these same three basic steps.

Before you start

You'll need Google Chrome. There's nothing to install.

If you'd like to try the optional axe DevTools exercise at the end, you'll also need the axe DevTools browser extension.

Why start with the browser console?

Most teams don't use the browser console for everyday accessibility testing. However, it's a useful way to understand how axe-core works before moving on to running tests from the command line.


What's in this tutorial

Getting started

Exploring the results

Trying another page


Getting started

Step 1. Open the test page

Open the axe test page in Chrome, since its console is the easiest to work with.

The page has 7 deliberately planted accessibility errors. The table below explains each issue, so you can compare them with axe-core's results.

Error What's wrong
1 The page doesn't say what language it's in
2 A heading jumps from level 1 to level 3
3 An image has no alt text
4 A text box has no label
5 A button has no text
6 A link has no text
7 Some grey text has low contrast

You should see a heading, a placeholder image, an empty text box, an empty button and an empty link, and some faint grey text. The button and link have red borders, so you can see where they are, even though they have no text.

Step 2. Open the console

Open Chrome's developer tools and go to the Console tab. This is where you'll run JavaScript directly in the browser.

Watch out for:

Step 3. Load axe-core into the page

Paste this code into the console and press Enter. It adds the axe-core script to the page. This works much like adding a <script> element to the HTML, but you're doing it while the page is open.

const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/axe-core@4.14.0/axe.min.js';
document.head.appendChild(script);

In plain terms:

  1. Make a new <script> element.
  2. Set its source to the axe-core file, which is hosted online.
  3. Add it to the page, so the browser downloads and runs it.

Watch out for:

Step 4. Check axe has loaded

Type this and press Enter:

axe.version

You should see '4.14.0'. This means axe-core has loaded into the page and is ready to run accessibility tests.

Watch out for:

Step 5. Run axe

Paste this and press Enter:

const results = await axe.run();

In plain terms: run axe on the whole page, wait for it to finish, and store the findings in something called results.

Watch out for:

Step 6. Look inside the results

Type this and press Enter:

results

Click the small triangle at the start of the line to expand it. Near the bottom, you'll find four groups, each with a count in brackets:

These four groups help you understand what axe-core found. It doesn't tell you whether a page is fully accessible, only what its automated checks could and couldn't confirm.

Watch out for:

Video walkthrough: Getting started

Transcript

Exploring the results

The following examples are optional. They show different ways to explore and present the results, and you can try them in any order.

Count the failed rules

Type this and press Enter:

results.violations.length

In plain terms: from results, find the violations group and use length to count how many items it contains. Each dot lets you access another part of the results.

You should see 7, one for each planted error.

Watch out for:

Show the violations as a table

Paste this and press Enter:

console.table(results.violations.map(v => ({
  rule: v.id,
  impact: v.impact,
  instances: v.nodes.length,
  help: v.help
})));

In plain terms: for each violation, find the rule's ID, how severe the issue is, how many elements are affected and a short description. Then show everything in a table.

You should see a table with 7 rows: 3 critical, 3 serious and 1 moderate.

Watch out for:

Filter to critical and serious

Paste this and press Enter:

results.violations
  .filter(v => v.impact === 'critical' || v.impact === 'serious')
  .forEach(v => console.log(v.impact, v.id));

In plain terms: keep only the violations that are critical or serious, then print each one on its own line.

You should see 6 lines. You won't see heading-order because its impact is moderate.

axe-core reports all the violations it finds. Tools that let you filter by severity use this information to decide which results to show.

Watch out for:

See which elements failed

Paste this and press Enter:

results.violations.forEach(v =>
  v.nodes.forEach(n => console.log(v.id, '|', n.target.join(' '), '|', n.html))
);

In plain terms: for each violation, go through every affected element and print the rule's ID, the element's CSS selector (the target) and its HTML.

You should see 7 lines, each showing the HTML of an affected element from your test page, such as <img src="https://placehold.co/200x200"> on the image-alt line. This answers the "where on the page?" question.

Watch out for:

Highlight the failing elements

Highlight the affected elements on the page, rather than just viewing them in the console. Paste this and press Enter:

results.violations.forEach(v =>
  v.nodes.forEach(n => {
    const el = document.querySelector(n.target[0]);
    if (el) el.style.outline = '4px dashed red';
  })
);

In plain terms: for each affected element, find it on the page using its CSS selector (the target), and draw a thick, dashed red outline around it.

You should see dashed red outlines around the seven affected elements: the heading, image, text box, button, link, faint text and the root HTML element (for the missing lang).

Watch out for:

Copy the results

Chrome's console has a built-in copy() command. Type this and press Enter:

copy(results)

In plain terms: copy everything in results to the clipboard.

The console will show undefined. Now paste the results into a text editor using Command + V on Mac or Ctrl + V on Windows. You'll see the full results as JSON.

This is the same kind of data that other axe tools work with. For example, the axe command-line tool can save accessibility test results as JSON. Both approaches use axe-core to run the tests.

Watch out for:

Open a full report in a new tab

Turn the results into a readable report and open it in a new browser tab. Paste this and press Enter:

{
  const escape = s => String(s ?? '')
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;');

  const order = { critical: 0, serious: 1, moderate: 2, minor: 3 };
  const violations = [...results.violations].sort((a, b) => order[a.impact] - order[b.impact]);

  const sections = violations.map(v => `
    <section>
      <h2>${escape(v.help)}</h2>
      <p><strong>Impact:</strong> ${escape(v.impact)}. <strong>Rule:</strong> <code>${escape(v.id)}</code></p>
      <p>${escape(v.description)}</p>
      <h3>Failing elements</h3>
      <ul>
        ${v.nodes.map(n => `
        <li>
          <pre><code>${escape(n.html)}</code></pre>
          <pre>${escape(n.failureSummary)}</pre>
        </li>`).join('')}
      </ul>
      <p><a href="${escape(v.helpUrl)}">Learn more about ${escape(v.id)}</a></p>
    </section>`).join('');

  const html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>axe report</title>
<style>
  body { font-family: system-ui, sans-serif; line-height: 1.5; max-width: 60rem; margin: 2rem auto; padding: 0 1rem; }
  section { border-top: 2px solid #ccc; margin-top: 2rem; }
  pre { background: #f4f4f4; padding: 0.75rem; white-space: pre-wrap; }
</style>
</head>
<body>
<main>
<h1>axe report</h1>
<p>Page: <code>${escape(results.url)}</code></p>
<p>${results.violations.length} violations, ${results.passes.length} passes, ${results.incomplete.length} need review.</p>
${sections}
</main>
</body>
</html>`;

  window.open(URL.createObjectURL(new Blob([html], { type: 'text/html' })));
}

In plain terms:

  1. escape makes sure HTML from the results is displayed as text, rather than becoming part of the report's HTML.
  2. The violations are sorted from critical to minor.
  3. Each violation becomes a section, with its description, impact, affected elements, details about what failed and a help link.
  4. The sections are added to a simple web page.
  5. window.open opens that page in a new tab.

You should see a new tab called "axe report", listing all 7 violations, with the critical ones first.

Watch out for:

Video walkthrough: Exploring the results

Transcript

Trying another page

Load and run axe-core on a new page

Now let's try axe-core on another page. There's a fixed version of the test page, with all seven accessibility issues corrected.

Open the fixed page in Chrome, then open the console.

Here's a quicker way to load axe-core and run the test. Instead of entering two separate commands, you can combine them into one:

const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/axe-core@4.14.0/axe.min.js';
const loaded = new Promise(resolve => script.onload = resolve);
document.head.appendChild(script);
await loaded;
const results = await axe.run();

Paste this into the console and press Enter.

The code loads axe-core, waits until it's ready, then runs the accessibility test and stores the results in results.

You can use this same approach on other pages, without having to enter the commands separately.

Watch out for:

Check the results

Let's see what axe-core found on the fixed page.

Type this and press Enter:

results.violations.length

You should see 0, meaning axe-core found no violations.

Now check how many rules passed:

results.passes.length

You should see more passes than on the original test page, because the corrected elements now pass the rules they failed before.

You can also try any of the examples from Exploring the results on this page.

Watch out for:

Video walkthrough: Trying another page

Transcript

Try it in axe DevTools

If you have the axe DevTools browser extension, run it on both test pages and compare the results. You should see the same issues, because the extension uses axe-core too.

If your counts don't match, check the extension's Best Practices setting. When it's turned off, heading-order won't appear, because it's a best practice rule rather than a WCAG requirement.


This tutorial is part of a series on accessibility testing with axe-core and Playwright. The articles in this series are:

  1. axe-core, Playwright and Cucumber
  2. Running axe-core in the browser console
  3. Setting up Node.js
  4. Running axe-core from the command line
  5. Running axe-core with a Playwright script COMING SOON
  6. Accessibility testing with Playwright