Skip to main content

a11y-ad — browserless WCAG 4.1.2 accessible-name auditor

a11y-ad audits HTML for missing accessible names (WCAG 4.1.2 / "Name, Role, Value") on every interactive element — no browser, no headless Chrome, stdlib only. Built for CI.

Why another accessibility tool?

Most lightweight linters check only aria-label/<label> and flag every button with visible text as a critical failure. In one audit run against a fully-labelled application, a popular scanner reported 68 "critical" HTML accessible-name issues — 0 of them real, because visible button text, aria-labelledby, and wrapping <label> elements are all valid accessible names.

a11y-ad resolves the name the way assistive technology does, following the WCAG 4.1.2 resolution order:

aria-labelledby -> aria-label -> visible text -> <label for=id>
-> wrapping <label> -> title -> alt (input[type=image])

and reports only the elements that end up with no name at all.

Scope (be honest about automated a11y coverage)

Automated tools in general catch roughly 30–50% of WCAG issues; manual keyboard/screen-reader testing remains necessary. a11y-ad covers exactly one failure class — missing accessible names — and does it with zero false positives in the resolution cases above. Browser-measured checks (target size, focus visibility, contrast, reduced motion) are out of scope for the core; see Roadmap.

Install

pip install a11y-ad

Usage

a11y-ad index.html            # single file
a11y-ad src/                  # directory (recursive, *.html)
a11y-ad https://example.com   # URL
a11y-ad src/ --json           # machine-readable

Exit code is 1 when any interactive element lacks a name — use it as a CI gate:

# GitHub Actions example
- run: pip install a11y-ad && a11y-ad dist/

JSON payload

[
  {
    "target": "bad.html",
    "total": 3,
    "missing": ["line 3 <button >", "line 4 <input >"],
    "sources": {"MISSING": 2, "text": 1},
    "ok": false
  }
]

Library

from a11y_ad import audit, audit_file, audit_tree, fetch

r = audit('<button aria-labelledby="h1">Edit</button>')
r.ok            # True
r.elements[0]   # Element(tag='button', name='...', source='aria-labelledby')
r.missing       # elements with no accessible name

Exclusions & semantics

  • <a> without href and <input type="hidden"> are not interactive → skipped.
  • aria-hidden="true" counts as named (it is intentionally hidden from AT).
  • Elements whose visible text lives in a translation dictionary (filled at runtime) will read as statically missing; audit the rendered DOM for the final word (see Roadmap).

Security

fetch() refuses non-http(s) schemes and any host resolving to a loopback, private, link-local, reserved or multicast address, so the library can be wired into "enter your URL" scan forms without handing callers a probe into the host network. The guard is best-effort (no DNS-rebinding defense) — for untrusted input at scale, front it with a proxy allowlist.

Roadmap

  • --i18n runtime layer: apply a key/value dictionary before auditing (ported from the battle-tested original).
  • GitHub Action wrapper.
  • Browser-measured WCAG 2.2 checks (2.5.8 target size, 2.4.7 focus, 1.4.3 contrast) as an optional [browser] extra (Playwright).

Provenance

The resolver is a hardened port of an auditor written for a production PWA, where four measurement bugs (void-tag stack shifting, early </span> closure bypassing a wrapping <label>, missing label[for] map, wrapping labels not recognized) were found via live measurement and fixed; their regression scenarios ship as tests in this repository.

License

Apache-2.0

Metadata

Release files for a11y-ad 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for a11y-ad 0.1.0
File Size Uploaded
a11y_ad-0.1.0.tar.gz 16.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for a11y-ad 0.1.0
File Interpreter ABI Platform
a11y_ad-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 30.3 kB

Release files / a11y_ad-0.1.0.tar.gz

Download URL a11y_ad-0.1.0.tar.gz
Size 16.9 kB
Tags Source
SHA-256 checksum
How to use checksums
9c482d64c401de9964d219817a7afffb3e5229295920de30160a92b1e3ed7412
BLAKE2b-256 checksum
How to use checksums
17f00097249ebe00077f350f5b4887ca46c0d68fbb89a97d7617dd00157bdc9f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release files / a11y_ad-0.1.0-py3-none-any.whl

Download URL a11y_ad-0.1.0-py3-none-any.whl
Size 13.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6a9940f7bdf4a6cd760cda14fe8ee3da9ae16ed79a788b3ec535bbb785b65eaf
BLAKE2b-256 checksum
How to use checksums
7ce143f2edc3d4e4b8eb15f23f3f1754ad515a61b3433de7aef5f4f61d6ad4a6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page