Axe rules
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.From its SKILL.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.
SKILL.md
11.5 KB, ~2.9k tokens by cl100k_base, 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/
What ships with it: 1 file
1.8 KB alongside SKILL.md
- SYNC.md1.8 KB