@diegovelasquezweb/a11y-engine 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +84 -0
- package/README.md +220 -7
- package/assets/engine/cdp-checks.json +30 -0
- package/assets/engine/pa11y-config.json +53 -0
- package/docs/architecture.md +218 -0
- package/docs/cli-handbook.md +237 -0
- package/docs/outputs.md +303 -0
- package/package.json +9 -2
- package/scripts/audit.mjs +3 -0
- package/scripts/core/asset-loader.mjs +4 -0
- package/scripts/engine/analyzer.mjs +8 -1
- package/scripts/engine/dom-scanner.mjs +366 -5
- package/scripts/index.mjs +262 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# CLI Handbook
|
|
2
|
+
|
|
3
|
+
**Navigation**: [Home](../README.md) • [Architecture](architecture.md) • [CLI Handbook](cli-handbook.md) • [Output Artifacts](outputs.md)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Basic usage](#basic-usage)
|
|
10
|
+
- [Prerequisites](#prerequisites)
|
|
11
|
+
- [Flag groups](#flag-groups)
|
|
12
|
+
- [Targeting & scope](#targeting--scope)
|
|
13
|
+
- [Audit intelligence](#audit-intelligence)
|
|
14
|
+
- [Execution & emulation](#execution--emulation)
|
|
15
|
+
- [Output generation](#output-generation)
|
|
16
|
+
- [Examples](#examples)
|
|
17
|
+
- [Exit codes](#exit-codes)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Basic usage
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx a11y-audit --base-url <url> [options]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Or if installed locally:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
node node_modules/@diegovelasquezweb/a11y-engine/scripts/audit.mjs --base-url <url> [options]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The only required flag is `--base-url`. All other flags are optional.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Prerequisites
|
|
38
|
+
|
|
39
|
+
The engine uses two separate browser installations:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Required — used by axe-core and CDP checks
|
|
43
|
+
npx playwright install chromium
|
|
44
|
+
|
|
45
|
+
# Required for pa11y — uses Puppeteer's Chrome (separate from Playwright)
|
|
46
|
+
npx puppeteer browsers install chrome
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
If Puppeteer Chrome is missing, pa11y checks fail silently (non-fatal) and the scan continues with axe-core + CDP only.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Flag groups
|
|
54
|
+
|
|
55
|
+
### Targeting & scope
|
|
56
|
+
|
|
57
|
+
Controls what gets scanned.
|
|
58
|
+
|
|
59
|
+
| Flag | Argument | Default | Description |
|
|
60
|
+
| :--- | :--- | :--- | :--- |
|
|
61
|
+
| `--base-url` | `<url>` | (Required) | Starting URL. Must include protocol (`https://` or `http://`). |
|
|
62
|
+
| `--max-routes` | `<num>` | `10` | Maximum unique same-origin paths to discover and scan. |
|
|
63
|
+
| `--crawl-depth` | `<num>` | `2` | How deep to follow links during BFS discovery (1-3). Has no effect when `--routes` is set. |
|
|
64
|
+
| `--routes` | `<csv>` | — | Explicit paths to scan (e.g. `/,/about,/contact`). Overrides auto-discovery entirely. |
|
|
65
|
+
| `--project-dir` | `<path>` | — | Path to the audited project source. Enables the source code pattern scanner and framework auto-detection from `package.json`. |
|
|
66
|
+
|
|
67
|
+
**Route discovery logic**:
|
|
68
|
+
1. If the target has a `sitemap.xml`, all listed URLs are used (up to `--max-routes`).
|
|
69
|
+
2. Otherwise, BFS crawl from `--base-url`, following same-origin `<a href>` links.
|
|
70
|
+
3. `--routes` always takes precedence over both.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
### Audit intelligence
|
|
75
|
+
|
|
76
|
+
Controls how findings are interpreted and filtered.
|
|
77
|
+
|
|
78
|
+
| Flag | Argument | Default | Description |
|
|
79
|
+
| :--- | :--- | :--- | :--- |
|
|
80
|
+
| `--target` | `<text>` | `WCAG 2.2 AA` | Compliance target label rendered in reports. Does not change which rules run. |
|
|
81
|
+
| `--only-rule` | `<id>` | — | Run a single axe rule ID only. Useful for focused re-audits after fixing a specific issue. |
|
|
82
|
+
| `--ignore-findings` | `<csv>` | — | Comma-separated list of axe rule IDs to suppress from output entirely. |
|
|
83
|
+
| `--exclude-selectors` | `<csv>` | — | CSS selectors to skip. Elements matching these selectors are excluded from axe scanning. |
|
|
84
|
+
| `--axe-tags` | `<csv>` | `wcag2a,wcag2aa,wcag21a,wcag21aa,wcag22a,wcag22aa` | axe-core WCAG tag filter. Also determines the pa11y standard (`WCAG2A`, `WCAG2AA`, or `WCAG2AAA`). |
|
|
85
|
+
| `--framework` | `<name>` | — | Override auto-detected framework. Affects which fix notes and source boundaries are applied. |
|
|
86
|
+
|
|
87
|
+
**Supported `--framework` values**: `nextjs`, `gatsby`, `react`, `nuxt`, `vue`, `angular`, `astro`, `svelte`, `shopify`, `wordpress`, `drupal`.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
### Execution & emulation
|
|
92
|
+
|
|
93
|
+
Controls browser behavior during scanning.
|
|
94
|
+
|
|
95
|
+
| Flag | Argument | Default | Description |
|
|
96
|
+
| :--- | :--- | :--- | :--- |
|
|
97
|
+
| `--color-scheme` | `light\|dark` | `light` | Emulates `prefers-color-scheme` media query. |
|
|
98
|
+
| `--wait-until` | `domcontentloaded\|load\|networkidle` | `domcontentloaded` | Playwright page load strategy. Use `networkidle` for SPAs with async rendering. |
|
|
99
|
+
| `--viewport` | `<WxH>` | `1280x800` | Browser viewport in pixels (e.g. `375x812` for mobile, `1440x900` for desktop). |
|
|
100
|
+
| `--wait-ms` | `<num>` | `2000` | Fixed delay (ms) after page load before the engines run. Useful when JS renders content after `DOMContentLoaded`. |
|
|
101
|
+
| `--timeout-ms` | `<num>` | `30000` | Network timeout per page load (ms). |
|
|
102
|
+
| `--headed` | — | `false` | Launch browser in visible mode. Useful for debugging page rendering issues. |
|
|
103
|
+
| `--affected-only` | — | `false` | Re-scan only routes that had violations in the previous scan. Reads `.audit/a11y-scan-results.json` to determine affected routes. Falls back to full scan if no prior results exist. |
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
### Output generation
|
|
108
|
+
|
|
109
|
+
Controls what artifacts are written.
|
|
110
|
+
|
|
111
|
+
| Flag | Argument | Default | Description |
|
|
112
|
+
| :--- | :--- | :--- | :--- |
|
|
113
|
+
| `--with-reports` | — | `false` | Generate full artifact set: `report.html`, `report.pdf`, `checklist.html`, and `remediation.md`. Requires `--output`. |
|
|
114
|
+
| `--skip-reports` | — | `true` | Default behavior. Only `remediation.md` is generated. |
|
|
115
|
+
| `--output` | `<path>` | — | Absolute or relative path for `report.html`. PDF and checklist are derived from the same directory. |
|
|
116
|
+
| `--skip-patterns` | — | `false` | Disable source code pattern scanner even when `--project-dir` is set. Use this for DOM-only results without static analysis. |
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Examples
|
|
121
|
+
|
|
122
|
+
### Minimal scan
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Produces only remediation.md in .audit/
|
|
126
|
+
a11y-audit --base-url https://example.com
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Full audit with all reports
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
a11y-audit \
|
|
133
|
+
--base-url https://example.com \
|
|
134
|
+
--with-reports \
|
|
135
|
+
--output ./audit/report.html
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Include source code intelligence
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
a11y-audit \
|
|
142
|
+
--base-url http://localhost:3000 \
|
|
143
|
+
--project-dir . \
|
|
144
|
+
--with-reports \
|
|
145
|
+
--output ./audit/report.html
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Focused re-audit — single rule, single route
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
a11y-audit \
|
|
152
|
+
--base-url https://example.com \
|
|
153
|
+
--only-rule color-contrast \
|
|
154
|
+
--routes /checkout \
|
|
155
|
+
--max-routes 1
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Fast re-audit after applying fixes
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
# Only re-scans routes that had violations in the last run
|
|
162
|
+
a11y-audit --base-url https://example.com --affected-only
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### SPA with deferred rendering
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
a11y-audit \
|
|
169
|
+
--base-url https://example.com \
|
|
170
|
+
--wait-until networkidle \
|
|
171
|
+
--wait-ms 3000
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Dark mode audit
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
a11y-audit --base-url https://example.com --color-scheme dark
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Mobile viewport
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
a11y-audit --base-url https://example.com --viewport 375x812
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Suppress known false positives
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
a11y-audit \
|
|
190
|
+
--base-url https://example.com \
|
|
191
|
+
--ignore-findings color-contrast,frame-title
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Explicit route list
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
a11y-audit \
|
|
198
|
+
--base-url https://example.com \
|
|
199
|
+
--routes /,/pricing,/blog,/contact
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Override framework detection
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
a11y-audit \
|
|
206
|
+
--base-url http://localhost:3000 \
|
|
207
|
+
--framework nextjs \
|
|
208
|
+
--project-dir .
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Custom axe-core WCAG tags
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# Only WCAG 2.0 A checks
|
|
215
|
+
a11y-audit --base-url https://example.com --axe-tags wcag2a
|
|
216
|
+
|
|
217
|
+
# Include AAA checks
|
|
218
|
+
a11y-audit --base-url https://example.com --axe-tags wcag2a,wcag2aa,wcag2aaa
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Exit codes
|
|
224
|
+
|
|
225
|
+
| Code | Meaning |
|
|
226
|
+
| :--- | :--- |
|
|
227
|
+
| `0` | Audit completed successfully (regardless of findings count) |
|
|
228
|
+
| `1` | Runtime error — invalid URL, missing required flag, stage failure, or timeout |
|
|
229
|
+
|
|
230
|
+
The engine never exits `1` just because findings were found. Exit `1` only indicates a pipeline or configuration error.
|
|
231
|
+
|
|
232
|
+
**Stdout markers** (parseable by scripts and CI):
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
REMEDIATION_PATH=<abs-path> # always printed on success
|
|
236
|
+
REPORT_PATH=<abs-path> # only printed when --with-reports is set
|
|
237
|
+
```
|
package/docs/outputs.md
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# Output Artifacts
|
|
2
|
+
|
|
3
|
+
**Navigation**: [Home](../README.md) • [Architecture](architecture.md) • [CLI Handbook](cli-handbook.md) • [Output Artifacts](outputs.md)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Default output directory](#default-output-directory)
|
|
10
|
+
- [progress.json](#progressjson)
|
|
11
|
+
- [a11y-scan-results.json](#a11y-scan-resultsjson)
|
|
12
|
+
- [a11y-findings.json](#a11y-findingsjson)
|
|
13
|
+
- [remediation.md](#remediationmd)
|
|
14
|
+
- [report.html](#reporthtml)
|
|
15
|
+
- [report.pdf](#reportpdf)
|
|
16
|
+
- [checklist.html](#checklisthtml)
|
|
17
|
+
- [Consuming outputs programmatically](#consuming-outputs-programmatically)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Default output directory
|
|
22
|
+
|
|
23
|
+
All artifacts are written to `.audit/` relative to the package root (`SKILL_ROOT`). When consumed as an npm package, this resolves to the real package path inside `node_modules/`.
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
.audit/
|
|
27
|
+
├── progress.json # real-time scan progress (per-engine steps + counts)
|
|
28
|
+
├── a11y-scan-results.json # merged raw results from axe + CDP + pa11y
|
|
29
|
+
├── a11y-findings.json # enriched findings (primary data artifact)
|
|
30
|
+
├── remediation.md # AI agent remediation guide
|
|
31
|
+
├── report.html # interactive dashboard (--with-reports)
|
|
32
|
+
├── report.pdf # compliance report (--with-reports)
|
|
33
|
+
├── checklist.html # manual testing checklist (--with-reports)
|
|
34
|
+
└── screenshots/ # element screenshots per violation
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
> When integrating the engine as a dependency (e.g. in `a11y-scanner`), use `fs.realpathSync` on the symlink path to resolve the real `.audit/` location — pnpm uses a deep `.pnpm/` directory structure, not the `node_modules/@scope/pkg` symlink.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## progress.json
|
|
42
|
+
|
|
43
|
+
Real-time scan progress written by `scripts/engine/dom-scanner.mjs` as each engine runs. Used by integrations for live progress UI.
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"steps": {
|
|
48
|
+
"page": { "status": "done", "updatedAt": "2026-03-14T14:02:50.609Z" },
|
|
49
|
+
"axe": { "status": "done", "updatedAt": "2026-03-14T14:02:51.389Z", "found": 8 },
|
|
50
|
+
"cdp": { "status": "done", "updatedAt": "2026-03-14T14:02:51.401Z", "found": 3 },
|
|
51
|
+
"pa11y": { "status": "done", "updatedAt": "2026-03-14T14:02:55.667Z", "found": 2 },
|
|
52
|
+
"merge": { "status": "done", "updatedAt": "2026-03-14T14:02:55.668Z", "axe": 8, "cdp": 3, "pa11y": 2, "merged": 11 }
|
|
53
|
+
},
|
|
54
|
+
"currentStep": "merge"
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Step keys
|
|
59
|
+
|
|
60
|
+
| Key | Engine | Description |
|
|
61
|
+
| :--- | :--- | :--- |
|
|
62
|
+
| `page` | — | Page navigation and load |
|
|
63
|
+
| `axe` | axe-core | axe-core WCAG rule scan. `found` = violation count. |
|
|
64
|
+
| `cdp` | CDP | Chrome DevTools Protocol accessibility tree check. `found` = issue count. |
|
|
65
|
+
| `pa11y` | pa11y | HTML CodeSniffer scan. `found` = issue count. |
|
|
66
|
+
| `merge` | — | Cross-engine merge and deduplication. `merged` = final unique count. |
|
|
67
|
+
|
|
68
|
+
### Step statuses
|
|
69
|
+
|
|
70
|
+
`pending` → `running` → `done` (or `error`)
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## a11y-scan-results.json
|
|
75
|
+
|
|
76
|
+
Merged results from all three engines (axe-core + CDP + pa11y) per route. Written by `scripts/engine/dom-scanner.mjs`.
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"generated_at": "2026-03-14T14:02:55.668Z",
|
|
81
|
+
"base_url": "https://example.com",
|
|
82
|
+
"projectContext": { "framework": "nextjs", "uiLibraries": ["radix-ui"] },
|
|
83
|
+
"routes": [
|
|
84
|
+
{
|
|
85
|
+
"path": "/",
|
|
86
|
+
"url": "https://example.com/",
|
|
87
|
+
"violations": [...],
|
|
88
|
+
"incomplete": [...],
|
|
89
|
+
"passes": [...]
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Each violation in the `violations` array includes a `source` field indicating which engine produced it (`undefined` for axe-core, `"cdp"` for CDP checks, `"pa11y"` for pa11y).
|
|
96
|
+
|
|
97
|
+
This file is consumed by `analyzer.mjs` and also used by `--affected-only` to determine which routes to re-scan on subsequent runs.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## a11y-findings.json
|
|
102
|
+
|
|
103
|
+
The primary enriched data artifact. Written by `scripts/engine/analyzer.mjs`. This is the file consumed by all report builders.
|
|
104
|
+
|
|
105
|
+
### Top-level structure
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"metadata": { ... },
|
|
110
|
+
"findings": [ ... ],
|
|
111
|
+
"incomplete_findings": [ ... ]
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### `metadata` fields
|
|
116
|
+
|
|
117
|
+
| Field | Type | Description |
|
|
118
|
+
| :--- | :--- | :--- |
|
|
119
|
+
| `scanDate` | `string` (ISO 8601) | Timestamp of when the scan ran |
|
|
120
|
+
| `checklist` | `object` | Manual check results if checklist was run |
|
|
121
|
+
| `projectContext` | `object` | Auto-detected framework, CMS, and UI libraries |
|
|
122
|
+
| `overallAssessment` | `string` | `"Pass"`, `"Conditional Pass"`, or `"Fail"` |
|
|
123
|
+
| `passedCriteria` | `string[]` | WCAG criterion IDs with no active violations |
|
|
124
|
+
| `outOfScope` | `object[]` | Findings excluded due to guardrails |
|
|
125
|
+
| `recommendations` | `object[]` | Grouped fix recommendations by component |
|
|
126
|
+
| `testingMethodology` | `object` | Scan scope and methodology summary |
|
|
127
|
+
| `fpFiltered` | `number` | Count of findings filtered as likely false positives |
|
|
128
|
+
| `deduplicatedCount` | `number` | Count of duplicate findings removed |
|
|
129
|
+
|
|
130
|
+
### `findings` — per-finding fields
|
|
131
|
+
|
|
132
|
+
| Field | Type | Description |
|
|
133
|
+
| :--- | :--- | :--- |
|
|
134
|
+
| `id` | `string` | Deterministic finding ID (e.g. `A11Y-001`) |
|
|
135
|
+
| `rule_id` | `string` | Rule ID from the source engine (e.g. `color-contrast`, `cdp-missing-accessible-name`, `pa11y-wcag2aa-...`) |
|
|
136
|
+
| `source_rule_id` | `string\|null` | Original rule ID when mapped from CDP/pa11y to axe equivalent |
|
|
137
|
+
| `title` | `string` | Human-readable finding title |
|
|
138
|
+
| `severity` | `string` | `Critical`, `Serious`, `Moderate`, or `Minor` |
|
|
139
|
+
| `wcag` | `string` | WCAG success criterion (e.g. `1.4.3`) |
|
|
140
|
+
| `wcag_criterion_id` | `string` | Full WCAG criterion ID (e.g. `1.4.3`) |
|
|
141
|
+
| `wcag_classification` | `string` | `A`, `AA`, `AAA`, or `Best Practice` |
|
|
142
|
+
| `area` | `string` | Affected page area (e.g. `Navigation`, `Forms`) |
|
|
143
|
+
| `url` | `string` | URL where the violation was found |
|
|
144
|
+
| `selector` | `string` | CSS selector for the affected element |
|
|
145
|
+
| `primary_selector` | `string` | Most stable selector chosen by the analyzer |
|
|
146
|
+
| `impacted_users` | `string` | Disability groups affected |
|
|
147
|
+
| `actual` | `string` | Observed violation description |
|
|
148
|
+
| `expected` | `string` | What the correct behavior should be |
|
|
149
|
+
| `category` | `string` | Violation category (e.g. `Color & Contrast`) |
|
|
150
|
+
| `primary_failure_mode` | `string\|null` | Root cause classification |
|
|
151
|
+
| `relationship_hint` | `string\|null` | Label/input relationship context |
|
|
152
|
+
| `failure_checks` | `object[]` | Engine check-level failure details |
|
|
153
|
+
| `related_context` | `object[]` | Surrounding DOM context |
|
|
154
|
+
| `fix_description` | `string\|null` | Plain-language fix explanation |
|
|
155
|
+
| `fix_code` | `string\|null` | Ready-to-apply code snippet |
|
|
156
|
+
| `fix_code_lang` | `string` | Language for code block (e.g. `html`, `jsx`) |
|
|
157
|
+
| `recommended_fix` | `string` | Link to canonical fix reference (APG, MDN) |
|
|
158
|
+
| `mdn` | `string\|null` | MDN documentation URL |
|
|
159
|
+
| `effort` | `string\|null` | Fix effort estimate (`low`, `medium`, `high`) |
|
|
160
|
+
| `related_rules` | `string[]` | Related rule IDs |
|
|
161
|
+
| `guardrails` | `object\|null` | Agent scope guardrails for this finding |
|
|
162
|
+
| `false_positive_risk` | `string\|null` | Known false positive patterns |
|
|
163
|
+
| `fix_difficulty_notes` | `string\|null` | Edge cases and pitfalls for this fix |
|
|
164
|
+
| `framework_notes` | `string\|null` | Framework-specific fix guidance |
|
|
165
|
+
| `cms_notes` | `string\|null` | CMS-specific fix guidance |
|
|
166
|
+
| `check_data` | `object\|null` | Raw engine check data |
|
|
167
|
+
| `total_instances` | `number` | Count of affected elements across all pages |
|
|
168
|
+
| `evidence` | `object[]` | DOM HTML snippets for each affected element |
|
|
169
|
+
| `screenshot_path` | `string\|null` | Path to element screenshot |
|
|
170
|
+
| `file_search_pattern` | `string\|null` | Regex/glob pattern to find the source file |
|
|
171
|
+
| `managed_by_library` | `string\|null` | UI library managing this element (if any) |
|
|
172
|
+
| `ownership_status` | `string` | `confirmed`, `potential`, or `unknown` |
|
|
173
|
+
| `ownership_reason` | `string\|null` | Why this ownership status was assigned |
|
|
174
|
+
| `primary_source_scope` | `string[]` | Source file paths likely containing the issue |
|
|
175
|
+
| `search_strategy` | `string` | Agent search strategy recommendation |
|
|
176
|
+
| `component_hint` | `string\|null` | Component name hint for source search |
|
|
177
|
+
| `verification_command` | `string\|null` | CLI command to verify fix was applied |
|
|
178
|
+
| `verification_command_fallback` | `string\|null` | Fallback verify command |
|
|
179
|
+
| `pages_affected` | `number\|null` | Number of pages with this violation |
|
|
180
|
+
| `affected_urls` | `string[]\|null` | All URLs where this violation appears |
|
|
181
|
+
|
|
182
|
+
### `incomplete_findings`
|
|
183
|
+
|
|
184
|
+
Violations that axe-core flagged as "needs review" (not confirmed pass or fail). Included for manual verification but not counted in the compliance score.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## remediation.md
|
|
189
|
+
|
|
190
|
+
AI agent-optimized remediation guide. Always generated (even without `--with-reports`). Written to `.audit/remediation.md`.
|
|
191
|
+
|
|
192
|
+
Content:
|
|
193
|
+
|
|
194
|
+
- Audit summary header with score, URL, and date
|
|
195
|
+
- Per-finding sections with: violation description, DOM evidence, fix description, code snippet, verify command, and WCAG criterion
|
|
196
|
+
- "Passed WCAG 2.2 Criteria" section listing clean criteria
|
|
197
|
+
- "Source Code Patterns" section (if source scanner ran)
|
|
198
|
+
- Component grouping table for batching fixes
|
|
199
|
+
|
|
200
|
+
The path is printed to stdout on completion as `REMEDIATION_PATH=<path>`.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## report.html
|
|
205
|
+
|
|
206
|
+
Interactive HTML dashboard. Generated only with `--with-reports --output <path>`.
|
|
207
|
+
|
|
208
|
+
Features:
|
|
209
|
+
|
|
210
|
+
- Severity-grouped findings with expandable detail cards
|
|
211
|
+
- DOM evidence with syntax-highlighted code
|
|
212
|
+
- Screenshot thumbnails per finding
|
|
213
|
+
- Search and filter by severity, category, and page
|
|
214
|
+
- Compliance score gauge with grade label
|
|
215
|
+
- Persona impact breakdown (visual, motor, cognitive, etc.)
|
|
216
|
+
- Quick wins section (Critical/Serious findings with ready code)
|
|
217
|
+
|
|
218
|
+
Written to the path specified by `--output`. The path is printed to stdout as `REPORT_PATH=<path>`.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## report.pdf
|
|
223
|
+
|
|
224
|
+
Formal PDF compliance report for stakeholders. Generated alongside `report.html` when `--with-reports` is set.
|
|
225
|
+
|
|
226
|
+
Sections:
|
|
227
|
+
|
|
228
|
+
1. Cover page with score, date, and target URL
|
|
229
|
+
2. Table of Contents
|
|
230
|
+
3. Executive Summary — score, overall assessment, finding counts by severity
|
|
231
|
+
4. Legal Risk Summary — applicable regulations based on score tier
|
|
232
|
+
5. Methodology — scan scope, tools, and WCAG version
|
|
233
|
+
6. Findings Breakdown — severity table and issue summary
|
|
234
|
+
7. Recommended Next Steps — dynamically generated from findings
|
|
235
|
+
|
|
236
|
+
Written to the same directory as `--output` with `.pdf` extension.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## checklist.html
|
|
241
|
+
|
|
242
|
+
Interactive manual testing checklist. Generated alongside `report.html` when `--with-reports` is set.
|
|
243
|
+
|
|
244
|
+
Contains the 41 WCAG 2.2 AA manual checks that automated tools cannot detect, including:
|
|
245
|
+
|
|
246
|
+
- Keyboard navigation and focus order
|
|
247
|
+
- Screen reader announcements
|
|
248
|
+
- Motion and animation
|
|
249
|
+
- Zoom and reflow (400%)
|
|
250
|
+
- Cognitive load and reading level
|
|
251
|
+
|
|
252
|
+
Each item is checkable and includes testing instructions. State is not persisted between sessions.
|
|
253
|
+
|
|
254
|
+
Written to the same directory as `--output` as `checklist.html`.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Consuming outputs programmatically
|
|
259
|
+
|
|
260
|
+
### Reading `a11y-findings.json` from an integration
|
|
261
|
+
|
|
262
|
+
```js
|
|
263
|
+
import fs from "node:fs";
|
|
264
|
+
import path from "node:path";
|
|
265
|
+
import { createRequire } from "node:module";
|
|
266
|
+
|
|
267
|
+
// Resolve real path (handles pnpm symlinks)
|
|
268
|
+
const req = createRequire(import.meta.url);
|
|
269
|
+
const auditScript = req.resolve("@diegovelasquezweb/a11y-engine/scripts/audit.mjs");
|
|
270
|
+
const engineRoot = path.dirname(path.dirname(auditScript));
|
|
271
|
+
const findingsPath = path.join(engineRoot, ".audit", "a11y-findings.json");
|
|
272
|
+
|
|
273
|
+
const { findings, metadata } = JSON.parse(fs.readFileSync(findingsPath, "utf-8"));
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
> Note: `import.meta.url` may be mangled by bundlers (e.g. Next.js). In that case, use `fs.realpathSync` on the known symlink instead:
|
|
277
|
+
|
|
278
|
+
```js
|
|
279
|
+
const symlinkBase = path.join(process.cwd(), "node_modules", "@diegovelasquezweb", "a11y-engine");
|
|
280
|
+
const engineRoot = fs.realpathSync(symlinkBase);
|
|
281
|
+
const findingsPath = path.join(engineRoot, ".audit", "a11y-findings.json");
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Reading `progress.json` for live UI updates
|
|
285
|
+
|
|
286
|
+
```js
|
|
287
|
+
const progressPath = path.join(engineRoot, ".audit", "progress.json");
|
|
288
|
+
|
|
289
|
+
// Poll this file during scan execution
|
|
290
|
+
if (fs.existsSync(progressPath)) {
|
|
291
|
+
const progress = JSON.parse(fs.readFileSync(progressPath, "utf-8"));
|
|
292
|
+
console.log(`Current step: ${progress.currentStep}`);
|
|
293
|
+
console.log(`axe found: ${progress.steps?.axe?.found ?? "pending"}`);
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Parsing stdout markers
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
OUTPUT=$(node scripts/audit.mjs --base-url https://example.com --with-reports --output ./audit/report.html)
|
|
301
|
+
REMEDIATION_PATH=$(echo "$OUTPUT" | grep REMEDIATION_PATH | cut -d= -f2)
|
|
302
|
+
REPORT_PATH=$(echo "$OUTPUT" | grep REPORT_PATH | cut -d= -f2)
|
|
303
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegovelasquezweb/a11y-engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "WCAG 2.2 AA accessibility audit engine — scanner, analyzer, and report builders",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -12,13 +12,19 @@
|
|
|
12
12
|
"engines": {
|
|
13
13
|
"node": ">=18"
|
|
14
14
|
},
|
|
15
|
+
"exports": {
|
|
16
|
+
".": "./scripts/index.mjs"
|
|
17
|
+
},
|
|
18
|
+
"main": "scripts/index.mjs",
|
|
15
19
|
"bin": {
|
|
16
20
|
"a11y-audit": "scripts/audit.mjs"
|
|
17
21
|
},
|
|
18
22
|
"files": [
|
|
19
23
|
"scripts/**",
|
|
20
24
|
"assets/**",
|
|
25
|
+
"docs/**",
|
|
21
26
|
"README.md",
|
|
27
|
+
"CHANGELOG.md",
|
|
22
28
|
"LICENSE"
|
|
23
29
|
],
|
|
24
30
|
"scripts": {
|
|
@@ -33,5 +39,6 @@
|
|
|
33
39
|
},
|
|
34
40
|
"devDependencies": {
|
|
35
41
|
"vitest": "^4.0.18"
|
|
36
|
-
}
|
|
42
|
+
},
|
|
43
|
+
"packageManager": "pnpm@10.22.0+sha512.bf049efe995b28f527fd2b41ae0474ce29186f7edcb3bf545087bd61fbbebb2bf75362d1307fda09c2d288e1e499787ac12d4fcb617a974718a6051f2eee741c"
|
|
37
44
|
}
|
package/scripts/audit.mjs
CHANGED
|
@@ -31,6 +31,7 @@ Targeting & Scope:
|
|
|
31
31
|
|
|
32
32
|
Audit Intelligence:
|
|
33
33
|
--target <text> Compliance target label (default: "WCAG 2.2 AA").
|
|
34
|
+
--axe-tags <csv> Comma-separated axe tags (e.g., wcag2a,wcag2aa).
|
|
34
35
|
--only-rule <id> Only check for this specific rule ID.
|
|
35
36
|
--ignore-findings <csv> Ignore specific rule IDs.
|
|
36
37
|
--exclude-selectors <csv> Exclude CSS selectors from scan.
|
|
@@ -136,6 +137,7 @@ async function main() {
|
|
|
136
137
|
const routes = getArgValue("routes");
|
|
137
138
|
const waitMs = getArgValue("wait-ms") || DEFAULTS.waitMs;
|
|
138
139
|
const timeoutMs = getArgValue("timeout-ms") || DEFAULTS.timeoutMs;
|
|
140
|
+
const axeTags = getArgValue("axe-tags");
|
|
139
141
|
|
|
140
142
|
const sessionFile = getInternalPath("a11y-session.json");
|
|
141
143
|
let projectDir = getArgValue("project-dir");
|
|
@@ -254,6 +256,7 @@ async function main() {
|
|
|
254
256
|
if (viewport) {
|
|
255
257
|
scanArgs.push("--viewport", `${viewport.width}x${viewport.height}`);
|
|
256
258
|
}
|
|
259
|
+
if (axeTags) scanArgs.push("--axe-tags", axeTags);
|
|
257
260
|
|
|
258
261
|
await runScript("engine/dom-scanner.mjs", scanArgs, childEnv);
|
|
259
262
|
|
|
@@ -34,6 +34,10 @@ export const ASSET_PATHS = {
|
|
|
34
34
|
),
|
|
35
35
|
codePatterns: path.join(ASSET_ROOT, "remediation", "code-patterns.json"),
|
|
36
36
|
},
|
|
37
|
+
engine: {
|
|
38
|
+
cdpChecks: path.join(ASSET_ROOT, "engine", "cdp-checks.json"),
|
|
39
|
+
pa11yConfig: path.join(ASSET_ROOT, "engine", "pa11y-config.json"),
|
|
40
|
+
},
|
|
37
41
|
reporting: {
|
|
38
42
|
wcagReference: path.join(ASSET_ROOT, "reporting", "wcag-reference.json"),
|
|
39
43
|
complianceConfig: path.join(
|
|
@@ -745,7 +745,12 @@ function computeTestingMethodology(payload) {
|
|
|
745
745
|
const scanned = routes.filter((r) => !r.error).length;
|
|
746
746
|
const errored = routes.filter((r) => r.error).length;
|
|
747
747
|
return {
|
|
748
|
-
automated_tools: [
|
|
748
|
+
automated_tools: [
|
|
749
|
+
"axe-core (via @axe-core/playwright)",
|
|
750
|
+
"CDP accessibility tree checks (Playwright)",
|
|
751
|
+
"pa11y (HTML CodeSniffer via Puppeteer)",
|
|
752
|
+
"Playwright + Chromium",
|
|
753
|
+
],
|
|
749
754
|
compliance_target: "WCAG 2.2 AA",
|
|
750
755
|
pages_scanned: scanned,
|
|
751
756
|
pages_errored: errored,
|
|
@@ -826,6 +831,8 @@ function buildFindings(inputPayload, cliArgs) {
|
|
|
826
831
|
findings.push({
|
|
827
832
|
id: "",
|
|
828
833
|
rule_id: v.id,
|
|
834
|
+
source_rule_id: v.source_rule_id || null,
|
|
835
|
+
source: v.source || "axe",
|
|
829
836
|
title: v.help,
|
|
830
837
|
severity: IMPACT_MAP[v.impact] || "Medium",
|
|
831
838
|
wcag: mapWcag(v.tags),
|