Accessibility audits
Run the bundled axe-core 4.14.0 against the current page with accessibilityAudit and read violations, incomplete checks and frame coverage.
page.goto("https://example.com/signup")
val report = page.accessibilityAudit(tags = setOf("wcag2a", "wcag2aa"), selector = "main")
println("axe ${report.axeVersion}: ${report.counts.violations} violations")
for (issue in report.violations) {
println("[${issue.impact}] ${issue.id}: ${issue.help} (${issue.nodeCount} nodes)")
issue.nodes.forEach { println(" ${it.target} ${it.failureSummary}") }
}
check(report.violations.none { it.impact == "critical" })page.accessibilityAudit(...) injects axe-core into the page, runs it, and returns a typed report. The engine ships inside KMPwright (unmodified axe-core 4.14.0, generated into kmpwright-core/.../generated/AxeBundle.kt), so nothing is downloaded at runtime and the page needs no network access.
This guide covers rule-based audits. For the accessibility tree that agents use to find elements (@e3 refs), see Snapshots.
Playwright has no built-in audit. Its docs suggest the @axe-core/playwright package, and accessibilityAudit covers the same use. The engine is injected privately: window.axe is left as the page had it.
Parameters
Prop
Type
Report
AccessibilityAuditReport:
| Field | Type | Meaning |
|---|---|---|
url | String | Document URL as axe reported it. |
axeVersion | String | Engine version (4.14.0). |
counts | AccessibilityAuditCounts | Rule counts by outcome: violations, incomplete, passes, inapplicable. |
violations | List<AccessibilityAuditIssue> | Rules that failed. |
incomplete | List<AccessibilityAuditIssue> | Rules axe could not decide and that need manual review. |
coverage | AccessibilityAuditCoverage | auditedFrames: Int and skippedFrames: List<String>. |
Only violations and incomplete are listed in detail. Passing and inapplicable rules appear only in counts.
AccessibilityAuditIssue:
| Field | Type | Meaning |
|---|---|---|
id | String | axe rule id, such as color-contrast or label. |
impact | String | minor, moderate, serious or critical; unknown when axe gives none. |
help | String | Short description of the rule. |
helpUrl | String | Deque University page for the rule. |
tags | List<String> | Rule tags (wcag2aa, cat.color, ...). |
nodeCount | Int | Total number of affected elements. |
nodes | List<AccessibilityAuditNode> | Up to maxNodesPerRule elements. |
AccessibilityAuditNode:
| Field | Type | Meaning |
|---|---|---|
target | JsonArray | axe's selector path for the element. Nested arrays step into shadow roots or frames. |
html | String | Element HTML, cut to 300 characters. |
failureSummary | String | axe's explanation of what to fix, cut to 16 KiB. |
The report classes are @Serializable, so you can write them out with kotlinx.serialization for CI artifacts.
Backends and frames
| Backend | accessibilityAudit capability | Frames |
|---|---|---|
| JVM (CDP) | Supported | Audits the top document and its iframes, each in its own isolated world, then merges the results with axe's runPartial/finishRun. selector applies to the top document only. Frames that can't be reached are listed in coverage.skippedFrames with the reason. Throws when there are more than 128 frames or more than 32 MiB of partial results. |
| Android WebView | Partial | Audits the main document only. Every iframe/frame is listed in coverage.skippedFrames by id, src or (unnamed frame). |
| iOS WKWebView | Partial | Same as Android. |
Errors
KmpwrightTimeoutExceptionwhen the audit exceedstimeoutMs.KmpwrightExceptionwhenKmpwrightConfig.allowDomExtractionorallowEvalis false, when the report is larger than 4 MiB, when the selector is invalid or matches nothing, or when the page has a locked (non-configurable)window.axeproperty.IllegalArgumentExceptionfor a non-positive timeout ormaxNodesPerRuleoutside 1..100.
Audits are checked against ActionType.Evaluate in the page's action policy. Concurrent audits on the same page run one at a time.
Related
- Snapshots: accessibility tree and
@eNrefs - Assertions
- Capabilities
Emulation
Emulate color scheme, geolocation, offline mode, network throttling, viewport, user agent, device scale factor and device presets, with what each backend does natively and what it fakes in JavaScript.
Reading, vitals, React and WebMCP
Fetch pages as Markdown with page.read and HttpReader, measure Core Web Vitals, inspect React component trees, and call WebMCP tools exposed by a page.