Accessibility test toolkit

Fix an axe violation

axe-core is the open-source rule engine most automated accessibility checkers use, and a1 axe runs its rules against a page. A violation has a rule, an impact, and the elements that failed the rule. Read the failing element, then map the rule to the success criterion it fails: one testable pass/fail rule inside the Web Content Accessibility Guidelines (WCAG), the standard most accessibility requirements come from. a1 wcag rule finds the criterion and the techniques that fix it.

Violation fields

a1 axe injects axe-core into the page and runs its rules there, so it sees the rendered DOM instead of the HTML a server sent. It returns four arrays: violations, passes, incomplete for checks axe could not decide on its own, and inapplicable for rules that do not apply to this page.

Each violation has the axe rule id, its impact, a description, help text and a help URL, the WCAG and other standard tags it maps to, and the nodes that failed.

a1 axe http://localhost:3000/checkout --json
"violations": [
  {
    "id": "button-name",
    "impact": "critical",
    "description": "Ensure buttons have discernible text",
    "help": "Buttons must have discernible text",
    "helpUrl": "https://dequeuniversity.com/rules/axe/4.13/button-name?application=axeAPI",
    "tags": ["cat.name-role-value", "wcag2a", "wcag412", "ACT", "..."],
    "nodes": [
      {
        "target": ["button[type=\"button\"]"],
        "html": "<button type=\"button\"><img src=\"cart.png\" alt=\"\"></button>",
        "failureSummary": "Fix any of the following:\n  Element does not have inner text that is visible to screen readers\n  aria-label attribute does not exist or is empty\n  ..."
      }
    ]
  }
]

Impact levels

Impact comes from axe-core, one fixed value per rule, not per WCAG criterion. It says how badly the rule's kind of failure affects a user, not how many users hit it on this page.

critical Blocks access outright. button-name and image-alt are critical: a control or image with no accessible name is unusable, not just harder to use.
serious color-contrast and link-name are serious: text is there, but a low-vision or screen reader user has real trouble getting to it.
moderate heading-order is moderate: a heading level that skips from 1 to 3 confuses navigation without blocking it.
minor empty-heading is minor: an empty heading adds noise to the structure without hiding content.

--fail-on <impact> on axe and audit counts only violations at or above that level toward the exit code, so --fail-on serious lets a page ship with open moderate and minor findings while still failing on critical and serious ones.

Failing nodes

Each entry in nodes is one element that failed the rule. target is a CSS selector array. Paste document.querySelector('button[type="button"]') into the browser console to find the element. html is the failing markup as axe saw it. failureSummary lists every way to satisfy the rule, not just the one this element is missing, since axe cannot tell which fix you will pick.

A rule can have more than one failing node, and axe reports every one it found, not just the first.

A page with fewer than 10 elements and under 50 characters of text adds a warning that a single-page app may not have mounted. See empty results.

Finding the fix

a1 wcag rule <ruleId> maps one axe rule to the criteria it fails, each with its conformance level (A, AA, or AAA), techniques, and failures, plus the axe help text and URL. a1 audit prints the command for you: every failing rule gets an entry in result.nextCommands.

a1 wcag rule button-name --json
"ruleId": "button-name",
"criteria": [
  {
    "id": "4.1.2",
    "title": "Name, Role, Value",
    "level": "A",
    "techniques": [
      { "id": "ARIA14", "title": "Using aria-label to provide an accessible name where a visible label cannot be used", "..." : "..." },
      { "id": "H44", "title": "Using label elements to associate text labels with form controls", "..." : "..." },
      "..."
    ],
    "failures": [
      { "id": "F68", "title": "Failure of Success Criterion 4.1.2 due to a user interface control not having a programmatically determined name" },
      "..."
    ]
  }
],
"description": "Ensure buttons have discernible text",
"help": "Buttons must have discernible text",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.13/button-name?application=axeAPI"

For the <button type="button"><img src="cart.png" alt=""></button> node above, the fix is ARIA14: add an aria-label to the button, or put visible text inside it. H44 applies to form controls that take a <label>, not to a button.

Rules without a criterion

a1 wcag rule heading-order returns 0, with an empty criteria array. axe tags heading-order as a best practice rather than a specific success criterion, so no page in WCAG decides whether to fix it. Fix it anyway.

A default a1 axe run covers only the rules mapped to a WCAG criterion, so axe runs a best-practice rule like this one only when you name it with --rule.