Fast, zero-DOM accessibility checker with axe-core compatible output
Project description
fast-a11y
Fast, zero-DOM accessibility checker with axe-core compatible output. Runs on raw HTML using static analysis -- no browser, no Selenium, no Playwright.
Why?
axe-core is the gold standard for accessibility testing, but it requires a full DOM environment (JSDOM or a real browser). For crawlers, CI pipelines, and build tools processing thousands of pages, that's a memory and performance bottleneck.
fast-a11y implements 86 WCAG rules using only Python's stdlib html.parser. It returns the exact same output format as axe-core, so it's a drop-in replacement.
| axe-core + browser | fast-a11y | |
|---|---|---|
| 1000 elements | ~200-500MB, ~2-5s | ~5MB, ~30ms |
| Requires browser/DOM | Yes | No |
| Output format | AxeResults | AxeResults (identical) |
| WCAG rules | ~95 | 86 |
| Dependencies | Heavy | Zero (stdlib only) |
Install
pip install fast-a11y
Usage
from fast_a11y import fast_a11y
html = """<!DOCTYPE html>
<html lang="en">
<head><title>My Page</title></head>
<body>
<img src="photo.jpg">
<a href="/page"></a>
</body>
</html>"""
results = fast_a11y(html)
print(results["violations"])
# [
# {"id": "image-alt", "impact": "critical", "nodes": [...]},
# {"id": "link-name", "impact": "serious", "nodes": [...]},
# ]
Options
from fast_a11y import fast_a11y
# Filter by WCAG tags (same as axe-core)
results = fast_a11y(html, {"runOnly": {"type": "tag", "values": ["wcag2a", "wcag2aa"]}})
# Filter by specific rules
results = fast_a11y(html, {"runOnly": {"type": "rule", "values": ["image-alt", "link-name"]}})
# Disable specific rules
results = fast_a11y(html, {"rules": {"color-contrast": {"enabled": False}}})
# Include URL in output
results = fast_a11y(html, url="https://example.com/page")
Output Format
The output is identical to axe-core's AxeResults:
{
"testEngine": {"name": "fast-a11y", "version": "0.1.0"},
"testRunner": {"name": "fast-a11y"},
"testEnvironment": {"userAgent": "", "windowWidth": 0, "windowHeight": 0},
"url": "",
"timestamp": "2026-01-01T00:00:00+00:00",
"toolOptions": {},
"passes": [...],
"violations": [...],
"incomplete": [...],
"inapplicable": [...],
}
Each RuleResult contains id, impact, tags, description, help, helpUrl, and nodes[] -- exactly matching axe-core.
Rules Covered (86)
Text Alternatives
image-alt, input-image-alt, object-alt, role-img-alt, svg-img-alt, area-alt, server-side-image-map
Language
html-has-lang, html-lang-valid, html-xml-lang-mismatch, valid-lang
Structure
document-title, definition-list, dlitem, list, listitem, heading-order, empty-heading, empty-table-header, duplicate-id, duplicate-id-aria, nested-interactive, page-has-heading-one
Forms
label, select-name, input-button-name, button-name, form-field-multiple-labels, autocomplete-valid, label-title-only
ARIA (25 rules)
aria-allowed-attr, aria-allowed-role, aria-hidden-body, aria-hidden-focus, aria-required-attr, aria-required-children, aria-required-parent, aria-roles, aria-valid-attr, aria-valid-attr-value, aria-roledescription, aria-input-field-name, aria-toggle-field-name, aria-command-name, aria-meter-name, aria-progressbar-name, aria-tooltip-name, aria-treeitem-name, aria-dialog-name, aria-text, aria-deprecated-role, aria-prohibited-attr, aria-braille-equivalent, aria-conditional-attr, presentation-role-conflict
Navigation
link-name, frame-title, frame-title-unique, bypass, tabindex, accesskeys, region
Media & Time
blink, marquee, meta-refresh, meta-refresh-no-exceptions, meta-viewport, meta-viewport-large, no-autoplay-audio, video-caption
Tables
td-headers-attr, th-has-data-cells, td-has-header, table-duplicate-name, table-fake-caption, scope-attr-valid
Landmarks
landmark-one-main, landmark-no-duplicate-main, landmark-no-duplicate-banner, landmark-no-duplicate-contentinfo, landmark-banner-is-top-level, landmark-contentinfo-is-top-level, landmark-complementary-is-top-level, landmark-main-is-top-level, landmark-unique
Color Contrast (best-effort)
color-contrast -- Checks inline styles and <style> blocks. Colors that can't be resolved statically (external CSS, var(), background images) are reported as incomplete rather than violations.
Rules NOT Covered (~9)
These rules fundamentally require a rendered DOM:
target-size-- requiresgetBoundingClientRect()link-in-text-block-- requires computed stylescss-orientation-lock-- requires CSS media query analysisp-as-heading-- requires computed font stylingscrollable-region-focusable-- requires overflow computationfocus-order-semantics-- requires tab order computationhidden-content-- requires full visibility computationlabel-content-name-mismatch-- requires rendered visible textframe-tested-- runtime axe concept
Replacing axe-core
If you're currently using axe-core with a browser:
# Before (axe-core + Playwright)
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.set_content(html)
results = page.evaluate("axe.run()")
# After (fast-a11y)
from fast_a11y import fast_a11y
results = fast_a11y(html)
Same output format. Synchronous. No browser. 100x less memory.
See Also
| Package | Description |
|---|---|
| @probeo/fast-a11y | TypeScript version of this package |
| workflow-py | Stage-based pipeline engine -- use fast-a11y as a step |
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fast_a11y_py-0.1.1.tar.gz.
File metadata
- Download URL: fast_a11y_py-0.1.1.tar.gz
- Upload date:
- Size: 49.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6fcf97c98b0a50c769748704b047d2f79c71765e57a2177a0f93991b79954209
|
|
| MD5 |
5749a807be51269e696d3028b8086671
|
|
| BLAKE2b-256 |
ee2f16827eb6b9b7f7ade8014b79a624f7199e29f84e5fde4332fb7b00082ffe
|
Provenance
The following attestation bundles were made for fast_a11y_py-0.1.1.tar.gz:
Publisher:
publish.yml on probeo-io/fast-a11y-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fast_a11y_py-0.1.1.tar.gz -
Subject digest:
6fcf97c98b0a50c769748704b047d2f79c71765e57a2177a0f93991b79954209 - Sigstore transparency entry: 1203659519
- Sigstore integration time:
-
Permalink:
probeo-io/fast-a11y-py@99c6dc788bd01804f1b331b7ba6344a827853315 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/probeo-io
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@99c6dc788bd01804f1b331b7ba6344a827853315 -
Trigger Event:
release
-
Statement type:
File details
Details for the file fast_a11y_py-0.1.1-py3-none-any.whl.
File metadata
- Download URL: fast_a11y_py-0.1.1-py3-none-any.whl
- Upload date:
- Size: 48.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
852116b16ff15730e4b9698f75ea62b3f87d3457e1321f6c37268a77fc6c0870
|
|
| MD5 |
f53df36ee9d59c2562ce9d5c31d5c6b7
|
|
| BLAKE2b-256 |
a0fbb5f90a91fdc9f282f4594e4b0985641926db4d84d4ee6d7784ba1ffb64af
|
Provenance
The following attestation bundles were made for fast_a11y_py-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on probeo-io/fast-a11y-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fast_a11y_py-0.1.1-py3-none-any.whl -
Subject digest:
852116b16ff15730e4b9698f75ea62b3f87d3457e1321f6c37268a77fc6c0870 - Sigstore transparency entry: 1203659520
- Sigstore integration time:
-
Permalink:
probeo-io/fast-a11y-py@99c6dc788bd01804f1b331b7ba6344a827853315 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/probeo-io
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@99c6dc788bd01804f1b331b7ba6344a827853315 -
Trigger Event:
release
-
Statement type: