Axe rules
A collection of Claude Skill to mirror https://mgifford.github.io/ACCESSIBILITY.md
npx -y skills add mgifford/accessibility-skills --skill axe-rulesAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its author says it does
Copied from the file, not written here
Load this skill when configuring axe-core scans, reviewing automated accessibility test results, or writing tests that use @axe-core/playwright, @axe-core/react, or similar integrations. Provides a quick reference to axe 4.x rule IDs, their WCAG mapping, and default severity levels.
SKILL.md
11.5 KB, as published. Nobody here has run it
Axe-Core Rules Reference Skill
Canonical source:
examples/AXE_RULES_REFERENCE.mdandexamples/AXE_RULES_COVERAGE.mdinmgifford/ACCESSIBILITY.mdThis skill is derived from those files. When in doubt, the example is authoritative. Note: the canonical files are organized around a specific test-fixture scanner project (numbered test pages). This skill re-presents the same rule set and severities as a general-purpose reference, independent of any particular fixture.
Apply these rules when writing, reviewing, or debugging axe-core scans.
Core Principle
Axe-core catches ~30–40% of WCAG issues automatically. It is the baseline, not the ceiling. Always pair automated scanning with manual keyboard and screen reader testing.
An axe-core result is evidence, not a conformance decision. Preserve the
rule's raw outcome (violations, incomplete, passes, inapplicable)
exactly as axe reports it — do not overwrite or discard it when converting
to a project finding. Cite this file, AXE_RULES_REFERENCE.md, or the
rule's own dequeuniversity.com/rules/axe/ page as the authority for a
WCAG mapping; do not invent a mapping axe-core does not document for that
rule. Record an unreviewed violations result as
evidence_status: automated-indicator with handling: review — never as a
confirmed standards failure — until a human confirms it. See
Accessibility Finding Tracking: Policy Classification.
Key tags to include in scans:
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa'])
Axe-core 4.11 includes 100+ automated checks across these categories: WCAG 2.0 A/AA (68 rules), WCAG 2.1 A/AA (26 rules), WCAG 2.2 A/AA (1 rule, disabled by default), Best Practices (30+ rules), WCAG AAA (3 rules, disabled by default), Experimental (9 rules, disabled by default), and Deprecated (7 rules, disabled by default).
Severity Scale (axe-core)
| axe severity | Meaning |
|---|---|
| critical | Blocks access for one or more disability groups |
| serious | Significantly impairs access |
| moderate | Creates friction; workaround exists |
| minor | Best-practice gap; marginal impact |
Critical Rules — must never appear in production
| Rule ID | Category | Description |
|---|---|---|
image-alt | Text alternatives | <img> missing alt attribute |
area-alt | Text alternatives | Image map <area> missing alt text |
input-image-alt | Text alternatives | <input type="image"> missing alt |
video-caption | Text alternatives | <video> without captions |
aria-allowed-attr | ARIA | Element has unsupported ARIA attributes for its role |
aria-hidden-body | ARIA | aria-hidden applied to <body> |
aria-required-attr | ARIA | Required ARIA attribute missing |
aria-required-children | ARIA | Required child ARIA roles missing |
aria-required-parent | ARIA | Required parent ARIA role missing |
aria-roles | ARIA | Invalid ARIA role value |
aria-valid-attr | ARIA | Invalid ARIA attribute name |
aria-valid-attr-value | ARIA | ARIA attribute has invalid value |
label | Forms | Form input without associated <label> |
select-name | Forms | <select> without accessible name |
button-name | Buttons/Links | Button without discernible text |
input-button-name | Buttons/Links | <input type="button"> without text |
duplicate-id-aria | Structure | Duplicate id values referenced by ARIA |
meta-refresh | Meta | <meta http-equiv="refresh"> causes timed redirect |
Serious Rules — fix before release
| Rule ID | Category | Description |
|---|---|---|
object-alt | Text alternatives | <object> without accessible text |
svg-img-alt | Text alternatives | SVG with role="img" and no accessible name |
role-img-alt | Text alternatives | Element with role="img" lacks alt text |
aria-braille-equivalent | ARIA | Braille label without non-braille equivalent |
aria-command-name | ARIA | Button, link, or menu item without accessible name |
aria-conditional-attr | ARIA | ARIA attribute used contrary to role spec |
aria-hidden-focus | ARIA | aria-hidden element contains focusable content |
aria-input-field-name | ARIA | ARIA input field without accessible name |
aria-meter-name | ARIA | ARIA meter element without accessible name |
aria-progressbar-name | ARIA | ARIA progressbar element without accessible name |
aria-prohibited-attr | ARIA | Prohibited ARIA attribute used |
aria-toggle-field-name | ARIA | ARIA toggle field without accessible name |
aria-tooltip-name | ARIA | ARIA tooltip without accessible name |
aria-dialog-name | ARIA | Dialog without accessible name |
aria-treeitem-name | ARIA | Tree item without accessible name |
color-contrast | Color | Text contrast below 4.5:1 (normal) or 3:1 (large) |
frame-title | Frames | <iframe> without accessible name |
frame-title-unique | Frames | Frame titles are not unique |
frame-focusable-content | Frames | Frame with focusable content is tabindex="-1" |
html-has-lang | Language | <html> missing lang attribute |
html-lang-valid | Language | lang attribute has invalid BCP 47 value |
valid-lang | Language | Inline lang value is invalid |
document-title | Language | <title> element missing |
list | Structure | <ul>/<ol> contains elements other than <li> |
listitem | Structure | <li> outside <ul> or <ol> |
definition-list | Structure | <dl> contains elements other than <dt>/<dd> |
dlitem | Structure | <dt>/<dd> outside <dl> |
bypass | Keyboard | No skip navigation mechanism present |
scrollable-region-focusable | Keyboard | Scrollable region not keyboard accessible |
link-name | Buttons/Links | Link without discernible text |
link-in-text-block | Buttons/Links | Link not distinguishable from surrounding text |
avoid-inline-spacing | Content sizing | Text spacing overrides not adjustable |
autocomplete-valid | Forms | autocomplete attribute invalid or missing |
tabindex | Best practices | tabindex value > 0 used |
label-title-only | Best practices | Form field labeled only via title attribute |
label-content-name-mismatch | Best practices (experimental) | Visible label text doesn't match accessible name |
td-has-header | Tables (experimental) | Large table cells lack associated headers |
table-fake-caption | Tables (experimental) | Table caption not marked up with <caption> |
p-as-heading | Best practices (experimental) | Paragraph styled to look like a heading |
Moderate Rules — fix in near-term backlog
| Rule ID | Category | Description |
|---|---|---|
heading-order | Headings | Heading levels skipped (e.g., h1 → h3) |
empty-heading | Headings | Heading has no discernible text |
th-has-data-cells | Tables | Table headers have no associated data cells |
td-headers-attr | Tables | Table cells reference invalid header IDs |
meta-viewport | Meta | Viewport user-scalable=no disables zoom |
html-xml-lang-mismatch | Language | lang and xml:lang attributes disagree |
form-field-multiple-labels | Forms | Form field has multiple conflicting labels |
landmark-one-main | Best practices | Page has no <main> landmark |
landmark-banner-is-top-level | Best practices | banner landmark not at top level |
landmark-contentinfo-is-top-level | Best practices | contentinfo landmark not at top level |
landmark-main-is-top-level | Best practices | main landmark not at top level |
landmark-no-duplicate-banner | Best practices | More than one banner landmark |
landmark-no-duplicate-contentinfo | Best practices | More than one contentinfo landmark |
landmark-no-duplicate-main | Best practices | More than one main landmark |
region | Best practices | Content exists outside landmark regions |
page-has-heading-one | Best practices | Page has no <h1> |
scope-attr-valid | Best practices | scope attribute has invalid value |
accesskeys | Best practices | accesskey values are not unique |
Minor Rules
| Rule ID | Category | Description |
|---|---|---|
table-duplicate-name | Tables | <caption> text duplicates summary attribute |
aria-deprecated-role | ARIA | Deprecated ARIA role used |
server-side-image-map | Keyboard | Server-side image map used |
aria-text | ARIA (best practice) | role="text" on an element with focusable descendants |
Disabled-by-default rules (deprecated marquee/blink/audio-caption) are not
listed above — flag them if encountered, but they are not enabled in a default scan.
The three WCAG AAA rules axe-core disables by default report against a
level the project's AA target does not require. If enabled and a project
has not declared a broader target, classify a confirmed finding from one of
these rules as obligation: aspirational, not advisory — it is still a
real AAA-level finding, just not part of the AA baseline. Do not report
satisfying one of these rules as WCAG AAA conformance.
Running axe-core in CI (Playwright)
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('No WCAG 2.x AA violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa'])
.analyze();
expect(results.violations).toEqual([]);
});
Common configuration options:
// Exclude a specific element (use sparingly — document the reason)
.exclude('#known-false-positive')
// Include only a specific element
.include('#component-under-test')
// Disable a specific rule (document the reason and JIRA/issue link)
.disableRules(['color-contrast'])
Definition of Done Checklist
- axe-core scans run on every PR with
wcag2a,wcag2aa,wcag21aa,wcag22aatags - Zero critical violations in production code
- Zero serious violations in production code (or explicit waiver with issue link)
- All rule exclusions documented with reason and associated issue
- Light and dark colour schemes both tested
- Mobile viewport tested
- axe results published as CI artifact for reference
Key WCAG Criteria (automation coverage)
- 1.1.1 Non-text Content (A)
- 1.3.1 Info and Relationships (A)
- 1.4.3 Contrast Minimum (AA)
- 2.4.2 Page Titled (A)
- 3.1.1 Language of Page (A)
- 4.1.2 Name, Role, Value (A)
- 4.1.3 Status Messages (AA) — partial coverage
Automation covers ~30–40% of WCAG issues. Always pair with manual testing.
References
- Full axe rules reference
- Axe rules coverage summary
- axe-core rule descriptions
- @axe-core/playwright
- axe-core GitHub
Standards horizon: These rules target WCAG 2.2 AA. Monitor: https://www.w3.org/TR/wcag-3.0/