Skip to main content

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 -- requires getBoundingClientRect()
  • link-in-text-block -- requires computed styles
  • css-orientation-lock -- requires CSS media query analysis
  • p-as-heading -- requires computed font styling
  • scrollable-region-focusable -- requires overflow computation
  • focus-order-semantics -- requires tab order computation
  • hidden-content -- requires full visibility computation
  • label-content-name-mismatch -- requires rendered visible text
  • frame-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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fast_a11y_py-0.1.1.tar.gz (49.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fast_a11y_py-0.1.1-py3-none-any.whl (48.3 kB view details)

Uploaded Python 3

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

Hashes for fast_a11y_py-0.1.1.tar.gz
Algorithm Hash digest
SHA256 6fcf97c98b0a50c769748704b047d2f79c71765e57a2177a0f93991b79954209
MD5 5749a807be51269e696d3028b8086671
BLAKE2b-256 ee2f16827eb6b9b7f7ade8014b79a624f7199e29f84e5fde4332fb7b00082ffe

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for fast_a11y_py-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 852116b16ff15730e4b9698f75ea62b3f87d3457e1321f6c37268a77fc6c0870
MD5 f53df36ee9d59c2562ce9d5c31d5c6b7
BLAKE2b-256 a0fbb5f90a91fdc9f282f4594e4b0985641926db4d84d4ee6d7784ba1ffb64af

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page