Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 44 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,40 @@
# @ngbracket/a11y-devtools

Accessibility auditing for Angular that maps each axe violation back to **the
component that rendered it**, so you get `♿ UserCardComponent — 2 issue(s)`, not a
wall of CSS selectors. Run it as a dev overlay while you build, or headless in CI.
Finds accessibility problems in Angular apps and names the component behind each one.
Use it as an overlay while you develop, or headless in CI. It runs axe-core, plus its
own keyboard checks if you turn them on, and groups the results by component:

```text
♿ UserCardComponent — 2 issue(s)
critical · image-alt: Images must have alternative text
serious · color-contrast: Elements must meet minimum color contrast ratio thresholds
```

**📖 Documentation: [ngbracket.com/tools/a11y-devtools/docs](https://ngbracket.com/tools/a11y-devtools/docs/introduction)**
(guides, every option and CLI flag, and the rule catalogue).
Documentation: [ngbracket.com/tools/a11y-devtools/docs](https://ngbracket.com/tools/a11y-devtools/docs/introduction)
has the guides, every option and CLI flag, and the rule catalogue. You can also try the
[live demo](https://a11y-demo.ngbracket.com/a11y-demo) without installing anything.

Part of the `@ngbracket` Angular tooling family.

## Features

- **Component attribution**: every finding names the Angular component to fix,
walking past UI-library components (Material, CDK, Nebular, …) to yours. Uses
- Component attribution: every finding names the Angular component to fix. It skips
past UI-library components (Material, CDK, Nebular and others) to yours, using
Angular's documented dev debug API (`window.ng`).
- **In-app provider**: rescans as the app settles; grouped console output and an
optional on-page overlay. Switch it on and off with the on-page pill or
Alt+Shift+A; the pill's menu picks what's drawn (highlights, tab order, focus
preview, minimum severity) and downloads an HTML report of the routes you've
visited. Choices are remembered.
- **Report mode**: scan many routes headless from the CLI; Markdown, JSON and a
self-contained HTML report.
- **CI gating with a baseline**: fail only on *new* issues, so an app with known
issues can adopt the gate today.
- **Dark mode**: scan your dark theme too, whether it follows the OS setting, a
class, an attribute, or custom logic.
- **Keyboard & Focus Mode**: tab-order visualisation, keyboard findings,
Focus preview (computed role, name and state), and keyboard-trap detection with real Tab presses.
- **Zero production weight**: a no-op in production; axe-core is never loaded.
- In-app provider: rescans each time the app settles, logs findings grouped by
component, and can draw an overlay on the page. Turn it on and off with the
on-page pill or Alt+Shift+A. The pill's menu picks what's drawn and downloads an
HTML report of the routes you've visited.
- Report mode: scans many routes headless from the CLI and writes Markdown, JSON
and a self-contained HTML report.
- CI gating with a baseline: fails only on new issues, so an app with known issues
can add the gate straight away.
- Dark mode: scans your dark theme too, however your app switches to it.
- Keyboard & Focus Mode: numbers the tab order, adds keyboard findings, shows the
Focus preview card (computed role, name and state), and finds keyboard traps by
pressing Tab for real.
- Production weight: does nothing in production builds, and axe-core isn't loaded
there.

## Install

Expand All @@ -38,7 +45,7 @@ npm i -D @ngbracket/a11y-devtools
npm i -D playwright && npx playwright install chromium
```

Needs Angular 18+.
It needs Angular 18 or later.

## Quick start

Expand All @@ -56,8 +63,8 @@ export const appConfig: ApplicationConfig = {
![Keyboard & Focus Mode over the demo page: numbered tab-order badges (badge 1 in
orange flags a positive tabindex) joined by a connector path, severity-coloured
finding highlights labelled with their owning component (including the ngbr/*
keyboard findings), and the focus-follow accessibility-tree panel showing the
focused button's computed role, name and states.](./demo/keyboard-layer-2026-09.png)
keyboard findings), and the Focus preview card showing the focused button's
computed role, name and states.](./demo/keyboard-layer-2026-09.png)

In CI (`--serve` starts `ng serve`, waits for it, and stops it after the scan):

Expand All @@ -78,13 +85,13 @@ Next steps, in the docs:
[CLI reference](https://ngbracket.com/tools/a11y-devtools/docs/cli) ·
[Findings & rules](https://ngbracket.com/tools/a11y-devtools/docs/findings)

## What it can't do
## What it leaves to you

Automated checks cover part of WCAG, so a clean run is necessary, not sufficient:
keyboard, screen-reader and human testing cover the rest. The keyboard findings are
heuristics, labelled "verify manually". It helps you build toward supporting WCAG
2.2 AA; it doesn't certify conformance. See
[What it checks (and what it can't)](https://ngbracket.com/tools/a11y-devtools/docs/coverage).
Automated checks cover part of WCAG. The rest needs manual testing: keyboard, screen
reader, zoom and review by people. The keyboard findings are heuristics,
each labelled "verify manually". The tool helps you build toward supporting WCAG 2.2
AA; it doesn't certify conformance. See
[What it checks](https://ngbracket.com/tools/a11y-devtools/docs/coverage).

## Develop

Expand All @@ -111,13 +118,13 @@ ever becomes reachable through a static import.

### Planned

- Headless "linear walkthrough" — the tab sequence as an SR-ish reading list per
route in report-mode (M2's preview is currently overlay-only).
- A headless "linear walkthrough": the tab sequence as a reading list per route in
report mode. The Focus preview is overlay-only for now.
- Per-component filtering and a violation-count badge.

### Done

- Component attribution — the nearest app-owned component, walking past UI primitives.
- Component attribution: the nearest app-owned component, skipping UI primitives.
- Directive and `hostDirectives` attribution.
- Configurable framework prefixes.
- axe scan.
Expand All @@ -128,10 +135,10 @@ ever becomes reachable through a static import.
- HTML report output.
- Baseline/diff mode: CI fails only on new issues.
- Dark-mode scanning: `--color-scheme`, `--dark-class` / `--dark-attribute`, and theme-aware hooks.
- **Keyboard & Focus Mode M1** — tab-order visualisation + keyboard-reachability findings.
- **Keyboard & Focus Mode M2** — focus-follow accessibility-tree preview.
- **Keyboard & Focus Mode M3** — missing focus-trap detection (uncontained
`aria-modal`) and the keyboard-trap walk with real Tab presses.
- Keyboard & Focus Mode M1: tab-order visualisation and keyboard-reachability findings.
- Keyboard & Focus Mode M2: the focus-follow accessibility-tree preview (now called Focus preview).
- Keyboard & Focus Mode M3: missing focus-trap detection (an `aria-modal` that doesn't
contain focus) and the keyboard-trap walk with real Tab presses.
- CI production-weight guard.
- Real-browser E2E tests, including component attribution in a real Angular app.
- Report mode can start the dev server (`--serve`).
Expand Down
54 changes: 26 additions & 28 deletions demo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ Three ways to see `@ngbracket/a11y-devtools` working.

## 1. Standalone overlay (no Angular app needed)

`index.html` feeds the overlay hand-made findings so you can see the *rendering*
— severity-coloured boxes, component-labelled chips, click-to-scroll — on a plain
page. The compiled `dist/overlay.js` is framework-agnostic, so no build tooling is
involved beyond `tsc`.
`index.html` feeds the overlay hand-made findings on a plain page, so you can see how
it draws them: severity-coloured boxes, chips labelled with the component, and
click-to-scroll. The compiled `dist/overlay.js` doesn't depend on Angular, so the
only build step is `tsc`.

```bash
npm run build
Expand All @@ -29,34 +29,33 @@ providers: [
];
```

On the live `/login` page the tool runs a real axe scan, attributes every
violation to its owning component, and overlays them — nine `region`/`landmark`
findings, each labelled (`Login`, `NgbrPasswordField`, `NgbrLoginForm`,
`NgbrAuthField`, `NgbrAuthDivider`):
On the `/login` page the tool runs an axe scan, finds the component that owns each
violation, and draws them on the page. Here that's nine `region` and `landmark`
findings, labelled `Login`, `NgbrPasswordField`, `NgbrLoginForm`, `NgbrAuthField`
and `NgbrAuthDivider`:

![end-to-end in the admin console](e2e-admin-login.jpeg)

This exercises the whole pipeline together: dynamic axe scan → `window.ng`
component attribution → grouped console reporter → overlay → zoneless-aware
rescan, all behind the dev-only provider (tree-shaken out of production).
That one page uses every stage: the axe scan, component attribution through
`window.ng`, the grouped console report, the overlay, and rescanning in a zoneless
app. All of it sits behind the dev-only provider, which is tree-shaken out of
production builds.

### All four severity colours

The admin app also ships a **dev-only** `/a11y-demo` route (registered only under
`isDevMode()`, so it never reaches production) that trips one axe rule per impact
level. It's the quickest way to see the overlay's full palette — critical (red),
serious (orange), moderate (yellow), minor (blue) — and the matching grouped
console report:
The admin app also has an `/a11y-demo` route, registered only under `isDevMode()`,
that fails one axe rule at each impact level, so you can see all four overlay
colours: critical (red), serious (orange), moderate (yellow) and minor (blue).

![all four overlay severity colours](all-severities.png)

The grouped, component-attributed console report the overlay writes alongside it —
one summary line, then findings grouped by the component that rendered each:
The console report written alongside it has one summary line, then the findings
grouped by the component that rendered each one:

![grouped, component-attributed console report](grouped-console-2026-09.png)

Run it across a real app, fix what it surfaces, and the report goes quiet — here's
the admin `/login` page after clearing every violation the tool found there:
After we fixed the violations it found on the admin `/login` page, the report for
that page is empty:

![console reporting no violations after the fixes](login-no-violations-console-2026-09.png)

Expand All @@ -83,11 +82,10 @@ and manual keyboard, screen-reader and content review is still required:

## Local-link caveat (integrators)

When **linking this package into an app locally** (not installing a published
version), give it a real directory under the app's `node_modules` containing only
`dist/` + `axe-core` — **not a plain symlink to the source checkout**. A symlink
drags in the package's own `@angular/core`/`rxjs`, so the app ends up with *two*
Angular instances; the `ENVIRONMENT_INITIALIZER` the provider registers then
belongs to the wrong instance and is silently ignored (no overlay, no scan). A
normal published `npm install` resolves those peers to the consumer's single copy
and avoids this entirely.
To link this package into an app locally instead of installing a published version,
create a real directory under the app's `node_modules` that contains only `dist/` and
`axe-core`. Don't symlink the source checkout. A symlink brings in the package's own
`@angular/core` and `rxjs`, so the app has two copies of Angular. The provider's
`ENVIRONMENT_INITIALIZER` then registers with the wrong copy and never runs, so you
get no overlay and no scan. A normal `npm install` of a published version uses the
app's own copy of those peers, so it doesn't have this problem.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@ngbracket/a11y-devtools",
"version": "0.15.6",
"description": "Dev-only in-app accessibility auditing for Angular that maps each axe violation back to the component that rendered it — the attribution React overlay tools can't do.",
"description": "Finds accessibility problems in Angular apps and names the component behind each one. A dev overlay, or headless reports for CI.",
"license": "MIT",
"author": "Duncan Faulkner",
"contributors": [
Expand Down
Loading