automatica11y 0.0.0-stage → 0.3.0

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.
Files changed (49) hide show
  1. package/AGENTS.md +32 -0
  2. package/LICENSE +21 -0
  3. package/README.md +136 -2
  4. package/bin/automatica11y.js +4 -0
  5. package/package.json +50 -4
  6. package/skills/automatica11y/SKILL.md +23 -0
  7. package/skills/automatica11y-runner/SKILL.md +181 -0
  8. package/skills/automatica11y-runner/references/fixtures.md +59 -0
  9. package/src/cli.js +49 -0
  10. package/src/commands/audit.js +4 -0
  11. package/src/commands/common.js +274 -0
  12. package/src/commands/compare.js +4 -0
  13. package/src/commands/doctor.js +20 -0
  14. package/src/commands/guide.js +45 -0
  15. package/src/env/browser.js +136 -0
  16. package/src/env/versions.js +60 -0
  17. package/src/globals.d.ts +10 -0
  18. package/src/harness/browser.js +20 -0
  19. package/src/harness/bundle.js +49 -0
  20. package/src/harness/npm-install.js +65 -0
  21. package/src/harness/npm-react.js +39 -0
  22. package/src/harness/npm-wc.js +30 -0
  23. package/src/harness/shadow.js +42 -0
  24. package/src/harness/static-serve.js +68 -0
  25. package/src/harness/storybook.js +116 -0
  26. package/src/harness/url.js +41 -0
  27. package/src/plan/build-plan.js +62 -0
  28. package/src/plan/classify.js +185 -0
  29. package/src/plan/mapping.js +101 -0
  30. package/src/plan/resolve-npm.js +87 -0
  31. package/src/report/comparison.js +183 -0
  32. package/src/report/index.js +10 -0
  33. package/src/report/parts.js +334 -0
  34. package/src/report/single.js +16 -0
  35. package/src/run/audit-npm.js +279 -0
  36. package/src/run/fail-check.js +62 -0
  37. package/src/run/pool.js +21 -0
  38. package/src/run/run-plan.js +262 -0
  39. package/src/run/summary.js +49 -0
  40. package/src/schema.js +255 -0
  41. package/src/text.js +9 -0
  42. package/src/tiers/interactions/archetypes.js +417 -0
  43. package/src/tiers/interactions/helpers.js +145 -0
  44. package/src/tiers/interactions/index.js +107 -0
  45. package/src/tiers/rules/axe.js +75 -0
  46. package/src/tiers/rules/canvas.js +34 -0
  47. package/src/tiers/rules/ibm.js +121 -0
  48. package/src/tiers/rules/index.js +81 -0
  49. package/src/tiers/vsr.js +134 -0
package/AGENTS.md ADDED
@@ -0,0 +1,32 @@
1
+ # automatica11y.
2
+
3
+ Instructions for AI agents working in this repository or asked to use this tool. This file ships in the npm package. The guide commands below print the copy that comes with the latest release, and the steps they print check that the tool's version matches the version those steps were written for.
4
+
5
+ ## If you're asked to check or compare accessibility.
6
+
7
+ Keep the user's request as they gave it, including the target and any settings they named, and carry it into the steps. Don't ask again for what they've already said. If the request doesn't name a target, ask for the target once, then continue with the steps. Use the defaults in the steps for any setting the user didn't name. Read the full steps and follow them exactly. They cover the version check, `doctor`, turning a request into an `audit` or `compare` command, writing fixtures for npm packages, and writing the report from `results.json`:
8
+
9
+ ```bash
10
+ npx --yes automatica11y@latest guide skill
11
+ ```
12
+
13
+ When the steps tell you to write a fixture, read the fixture guide:
14
+
15
+ ```bash
16
+ npx --yes automatica11y@latest guide fixtures
17
+ ```
18
+
19
+ If either guide command fails, stop. Report the exact error to the user, and don't produce an audit or compare result from memory. If the steps report a problem themselves, such as `doctor` finding no browser, follow what they say. If the version check in the steps reports a mismatch, stop. Report the installed version and the version the steps expect, and don't produce an audit or compare result until the user confirms how to proceed.
20
+
21
+ Both print files that live in `skills/automatica11y-runner/` in the repository and in the package. If you can read those files directly, you can read them instead of running the guide commands. That's only another way to get the text. If a guide command has already failed, stop as described above. Reading the files doesn't change that. Don't work from memory or from this file alone. The steps are the single source for what to run and for how to word results.
22
+
23
+ ## If you're changing the code (in a checkout of the repository).
24
+
25
+ - Run `npm test` (about two minutes, needs Chrome or Chromium) and `npm run lint` before you finish.
26
+ - If `npm test` can't run because Chrome or Chromium is missing, or if either command fails, stop. Report the exact error and the command that failed, and don't report the change as complete. The browser tests skip themselves when no browser is found, so a run that prints skipped tests isn't a pass either. Report the skipped count, and don't call the change complete.
27
+ - The code is plain ESM JavaScript with JSDoc types and no build step. It needs Node 20 or newer.
28
+ - `src/` holds the tool. `test/` holds the tests. `test/fixtures/` holds test pages and fake packages, which are not the fixtures a user writes for an npm package.
29
+ - Heavy dependencies (Playwright, esbuild, the rule engines) load only when a command needs them. A test checks that `--version`, `doctor`, `guide`, and `--plan` never import them.
30
+ - Keep findings from axe-core and IBM Equal Access separate. Never add their counts together or convert one engine's scale into the other's.
31
+ - A gap, an error, a failed target, or a result that isn't testable is a finding. It never counts as a pass. A report says "no automated violations found" only where an engine found none, and never says "accessible."
32
+ - There are two skills. `skills/automatica11y/` is a tiny bootstrap that people copy. It sends an agent to `guide`. `skills/automatica11y-runner/` holds the full steps and ships with the tool. The runner names the version series it works with. The series is the major and minor version while the major version is 0 (`0.2`), and the major version alone from 1.0 on. Update the series named in `skills/automatica11y-runner/SKILL.md` (written like `0.2.x`) whenever the series of the version in `package.json` changes, for example from `0.2.x` to `0.3.0`. A change from `0.2.5` to `0.2.6` needs no update. A test fails if they disagree.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Danny Wahl
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,137 @@
1
- # Temporary Holding Version
1
+ # automatica11y.
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Test and compare the accessibility of web pages, Storybook builds, and npm component libraries.
4
+
5
+ automatica11y answers two questions:
6
+
7
+ - How accessible is this? (`audit`)
8
+ - How do these compare? (`compare`, two or more targets)
9
+
10
+ It's **not** an attestation or certification tool. Automated checks cover only part of WCAG, so a report says "no automated violations found" only where an engine found none, and never says "accessible." See [Limits](#limits).
11
+
12
+ ## Quick start.
13
+
14
+ You need Node 20 or newer and Chrome or Chromium.
15
+
16
+ ```bash
17
+ npx automatica11y doctor
18
+ npx automatica11y audit https://example.com
19
+ npx automatica11y compare radix=npm:@radix-ui/react-dialog aria=npm:react-aria-components
20
+ ```
21
+
22
+ `doctor` checks your setup. If it can't find a browser, it prints the command that installs one:
23
+
24
+ ```bash
25
+ npx playwright-core install --only-shell chromium
26
+ ```
27
+
28
+ Each run writes a folder (`./a11y-report` by default) with `report.md`, `results.json`, and `plan.json`.
29
+
30
+ ## Targets.
31
+
32
+ A target is `[label=]<spec>`. The label is optional, and names the target in the report.
33
+
34
+ | Target | Spec |
35
+ |---|---|
36
+ | A live page | `https://example.com/page` |
37
+ | A local page or site | `./page.html` or `./dist`. A path with no prefix is relative to the working folder, so `dist` means `./dist`. Prefixes `../`, `/`, `~`, and `file:` work too. |
38
+ | A Storybook | Its URL, or a local folder with `index.json` or `stories.json`. |
39
+ | An npm package | `npm:name`, `npm:@scope/name`, or `npm:name@version`. React and web component libraries work. |
40
+
41
+ A bare word such as `button` is a path: the folder or file `./button`. Write `npm:button` to pick the package. The prefix is what chooses a package, so a folder with the same name never gets in the way. A bare word that isn't a path fails with a hint, such as "If you meant the npm package, write npm:react."
42
+
43
+ A local `.html` file is served over `http://localhost`, never `file://`. A static site audits its `index.html` only.
44
+
45
+ ## What it checks.
46
+
47
+ Three tiers run by default. Use `--tiers` to pick fewer.
48
+
49
+ - **Rules.** axe-core and IBM Equal Access run side by side. They overlap, and each catches things the other misses. Their findings are reported separately and never added together. axe-core reports an impact (`minor` to `critical`). IBM reports its Toolkit level, a staged adoption scale where level 1 is essential, high-impact requirements. The two scales aren't comparable.
50
+ - **Interactions.** Keyboard and focus checks for nine archetypes: button, link, dialog, menu, tabs, combobox, form-field, accordion, and tooltip. Each check runs on a fresh page.
51
+ - **Virtual screen reader.** The announcements a simulated screen reader makes, recorded as data. The output is simulated. It isn't a real screen reader, and real ones announce things differently.
52
+
53
+ Every result says what it ran, or why it didn't. A gap, a failure, or a result that can't be tested is a finding. It never counts as a pass.
54
+
55
+ ## Options.
56
+
57
+ | Option | Default | What it does |
58
+ |---|---|---|
59
+ | `--wcag 2.0\|2.1\|2.2` | `2.2` | The WCAG version. |
60
+ | `--level A\|AA\|AAA` | `AA` | The conformance level. IBM Equal Access has no AAA rules, so it runs its AA rules and says so. |
61
+ | `--engine axe,ibm` | both | Which rule engines run. |
62
+ | `--tiers rules,interactions,vsr` | all | Which tiers run. |
63
+ | `--archetypes a,b` | all | Limit npm and Storybook targets to these archetypes. |
64
+ | `--lib-a11y on,off` | both | For libraries with opt-in accessibility features. See [the fixture guide](skills/automatica11y-runner/references/fixtures.md). |
65
+ | `--mapping <file>` | none | A mapping file for npm targets. |
66
+ | `--max-stories <n>` | `200` | The Storybook story cap. The cap spreads over components. |
67
+ | `--out <dir>` | `./a11y-report` | Where results go. |
68
+ | `--plan` | off | Classify the targets and write `plan.json`, then stop. Nothing installs and no browser launches. |
69
+ | `--fail-on-axe <impact>` | off | Exit 1 if axe-core reports a violation at or above `minor`, `moderate`, `serious`, or `critical`. |
70
+ | `--fail-on-ibm <1\|2\|3>` | off | Exit 1 if IBM reports a violation at or below that Toolkit level. |
71
+ | `--fail-mode any\|all` | `any` | `any` trips when either engine's check trips. `all` trips only when both do, on the same target. |
72
+
73
+ Run `automatica11y run --plan <plan.json>` to repeat a saved plan. It warns if tool versions have changed.
74
+
75
+ ## Output.
76
+
77
+ - `report.md` is a complete report, written without any model. It opens with a coverage matrix, then lists findings, then ends with a method note.
78
+ - `results.json` holds everything, including element selectors and announcement logs.
79
+ - `plan.json` records what ran and the resolved tool and package versions.
80
+ - `mapping.json` appears for npm targets. See below.
81
+
82
+ ## Exit codes.
83
+
84
+ | Code | Meaning |
85
+ |---|---|
86
+ | 0 | The run completed. Findings don't change this unless a fail flag is set. |
87
+ | 1 | The run completed and a fail check tripped. |
88
+ | 2 | The command line was wrong. |
89
+ | 3 | An environment problem, such as a missing browser or an old Node. The message says how to fix it. |
90
+ | 4 | No target produced results. |
91
+
92
+ One failing target doesn't stop a comparison. It's recorded with its reason, and the others run.
93
+
94
+ ## npm packages and fixtures.
95
+
96
+ The tool installs each package into its own temporary folder (with install scripts turned off), loads it in the browser, and finds its exports or custom elements. It writes its guesses to `mapping.json`.
97
+
98
+ It builds `button` and `link` tests on its own. Every other archetype is built from parts that differ by library (`Dialog.Root`, `Dialog.Trigger`, and so on), so it needs a **fixture**: a small file you or your agent write, following [the fixture guide](skills/automatica11y-runner/references/fixtures.md). Put fixtures at `fixtures/<target id>/<archetype>.jsx` (`.js` for web components) and run again. An archetype without a fixture is a gap in the report.
99
+
100
+ Fixtures are code that the tool bundles and runs in a browser on your machine. Write them from the library's documentation, and read ones you didn't write.
101
+
102
+ ## Use it with an AI agent.
103
+
104
+ The tool prints its own guidance, so any agent that can run `npx` can learn to use it:
105
+
106
+ ```bash
107
+ npx automatica11y@latest guide # where to start (the AGENTS.md file)
108
+ npx automatica11y@latest guide skill # the full steps: build the command, run it, write the report
109
+ npx automatica11y@latest guide fixtures # how to write the fixtures an npm package needs
110
+ ```
111
+
112
+ The guidance ships with the tool, so it always matches the version you run. Tell your agent to run `npx automatica11y@latest guide` and follow it, then ask for things like "How accessible is Radix Dialog?" or "Compare the accessibility of React Aria and Headless UI." The agent needs to run shell commands and read and write files. Nothing here is tied to one agent.
113
+
114
+ **Skills.** If your agent loads skills from a folder, copy [`skills/automatica11y`](skills/automatica11y) into it. That's one small file, `SKILL.md`. It advertises the tool to the agent, and sends it to `guide`. It names no version, so it doesn't go stale. The full steps are the [`automatica11y-runner`](skills/automatica11y-runner) skill, which ships in the package and is what `guide skill` prints. Copy it too if you want the steps available without the network.
115
+
116
+ **AGENTS.md.** [`AGENTS.md`](AGENTS.md) is for agents that read it but don't load skills. It points to the same steps, and tells contributors how to run and change the code.
117
+
118
+ ## Limits.
119
+
120
+ - Automated rules find only part of what WCAG covers. They can't judge whether alt text is meaningful, whether link and heading text make sense in context, cognitive load, real focus and reading order in use, or how real screen readers behave. A person has to check those.
121
+ - Components are tested in the states a fixture shows. Dialogs, menus, tooltips, and comboboxes run closed and open. Other states aren't visited.
122
+ - Content on a canvas with no alternative, or inside a closed shadow root, can't be tested, and the report says so. The virtual screen reader can't read inside shadow roots at all.
123
+ - Vue, Svelte, Angular, and other frameworks report "unsupported framework."
124
+ - Native screen readers aren't part of this version.
125
+ - Results are a snapshot. The tools run at their latest versions, and the report records them.
126
+
127
+ ## Using it in CI.
128
+
129
+ ```bash
130
+ npx automatica11y audit ./dist --fail-on-axe serious --fail-on-ibm 1 --fail-mode any
131
+ ```
132
+
133
+ The run exits 1 when the check trips. Needs-review items never trip it.
134
+
135
+ ## License.
136
+
137
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { main } from "../src/cli.js";
3
+
4
+ process.exitCode = await main(process.argv.slice(2));
package/package.json CHANGED
@@ -1,6 +1,52 @@
1
1
  {
2
2
  "name": "automatica11y",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.3.0",
4
+ "description": "Test and compare the accessibility of web pages, Storybook builds, and npm component libraries.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "automatica11y": "bin/automatica11y.js"
9
+ },
10
+ "files": [
11
+ "bin",
12
+ "src",
13
+ "AGENTS.md",
14
+ "skills"
15
+ ],
16
+ "engines": {
17
+ "node": ">=20"
18
+ },
19
+ "scripts": {
20
+ "test": "node --test test/*.test.js",
21
+ "lint": "tsc -p jsconfig.json",
22
+ "prepublishOnly": "npm run lint"
23
+ },
24
+ "dependencies": {
25
+ "@axe-core/playwright": "^4.13.0",
26
+ "@guidepup/virtual-screen-reader": "^0.33.0",
27
+ "accessibility-checker-engine": "^4.0.34",
28
+ "axe-core": "^4.14.0",
29
+ "esbuild": "^0.28.2",
30
+ "playwright-core": "^1.64.0",
31
+ "valibot": "^1.5.0"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^26.6.4",
35
+ "react": "^19.3.0",
36
+ "react-dom": "^19.3.0",
37
+ "typescript": "^7.0.2"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "keywords": [
43
+ "accessibility",
44
+ "a11y",
45
+ "wcag",
46
+ "axe-core",
47
+ "testing",
48
+ "storybook",
49
+ "web-components",
50
+ "react"
51
+ ]
52
+ }
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: automatica11y
3
+ compatibility: Needs Node 20 or newer, a shell that can run npx, and network access to the npm registry.
4
+ description: Test and compare web accessibility. Use when someone asks how accessible a web page, Storybook, or npm component library is, asks for an accessibility or WCAG check or audit, or asks to compare the accessibility of two or more sites or component libraries (for example "how accessible is Radix Dialog?" or "compare React Aria and Headless UI").
5
+ ---
6
+
7
+ # automatica11y
8
+
9
+ automatica11y tests and compares web accessibility. This skill only gets you started. The full steps ship with the tool, so they always match its version.
10
+
11
+ 1. Check `node --version`. It must be 20 or newer. If it isn't, tell the user and stop. If the command fails or prints no version number, tell the user that Node.js 20 or newer couldn't be verified, include the error text, and stop.
12
+ 2. Keep the user's request as they gave it: the target or targets, and any settings they named, such as a WCAG version or level. You'll apply them after you read the guide.
13
+ 3. Run this, read all of what it prints, and follow it:
14
+
15
+ ```bash
16
+ npx --yes automatica11y@latest guide
17
+ ```
18
+
19
+ If the command exits with an error or prints no usable output, tell the user it failed, include the error text, and stop. Don't continue with partial instructions.
20
+
21
+ After reading the guide and the steps it sends you to, pass only settings they list as supported. If the user named a setting they don't list as supported, tell the user which setting is unsupported, list the supported values from the guide, and ask which to use. Don't ask again for anything they've already said.
22
+
23
+ 4. If you can't run shell commands, or `npx` can't reach the npm registry, tell the user and stop. Don't guess at results.
@@ -0,0 +1,181 @@
1
+ ---
2
+ name: automatica11y-runner
3
+ compatibility: Needs Node 20 or newer, Chrome or Chromium, a shell that can run npx, and network access to the npm registry.
4
+ description: The full steps for running automatica11y audits and comparisons, from building the command to writing the report. Use it when the automatica11y skill or AGENTS.md sends you here, or when you've already been told to run automatica11y and need the steps.
5
+ ---
6
+
7
+ # automatica11y runner
8
+
9
+ This skill turns a request into an `automatica11y` command, runs it, and writes a report from the results. The tool does the testing. This skill never guesses at results.
10
+
11
+ It's plain Markdown, and it isn't tied to one agent. It needs an agent that can run shell commands (including `npx`) and read and write files. Paths such as `references/fixtures.md` are relative to this file.
12
+
13
+ The tool is **not** an attestation or certification tool. Automated checks cover only part of WCAG. Never say a target is "accessible" or "compliant." Where an engine reports zero violations for a target, say "no automated violations found" for that engine. Where it reports violations, list them as the tool reports them, and don't use that phrase for that engine.
14
+
15
+ ## 1. Check the version
16
+
17
+ This skill works with automatica11y **0.2.x**. Run:
18
+
19
+ ```bash
20
+ npx --yes automatica11y@latest --version
21
+ ```
22
+
23
+ If the command exits with an error or prints nothing, tell the user it failed, include the error text, and stop. If the output doesn't start with `0.2.`, stop. Tell the user the version you got and the series this copy expects (`0.2.x`). If this copy came from a file, offer to read the matching steps with `npx --yes automatica11y@latest guide skill`. Don't run an audit until the user confirms how to proceed.
24
+
25
+ ## 2. Check the setup
26
+
27
+ ```bash
28
+ npx --yes automatica11y@latest doctor
29
+ ```
30
+
31
+ Relay any fix it prints, then stop until the user has applied it. Exit code 3 means a problem with the environment: Node older than 20, or no Chrome or Chromium. Don't install or configure anything yourself. That logic lives in the tool.
32
+
33
+ ## 3. Turn the request into a command
34
+
35
+ Only two commands run audits:
36
+
37
+ ```bash
38
+ npx --yes automatica11y@latest audit <target> [options]
39
+ npx --yes automatica11y@latest compare <target> <target> [<target>...] [options]
40
+ ```
41
+
42
+ Use `audit` for one target and `compare` for two or more. A target is `[label=]<spec>`. The label is optional and names the target in the report.
43
+
44
+ | The user means | The spec is |
45
+ |---|---|
46
+ | A live page | `https://example.com/page` |
47
+ | A local page or site | `./page.html` or `./dist`. A path with no prefix is relative to the working folder. |
48
+ | A Storybook | its URL, or a local folder with `index.json` |
49
+ | An npm package | `npm:name`, `npm:@scope/name`, or `npm:name@version` |
50
+
51
+ A bare word such as `button` is a path: the folder or file `./button`. Always write an npm package with the `npm:` prefix. If a word could mean a package or a folder, ask the user which one before you run anything.
52
+
53
+ Options you can set, and nothing else:
54
+
55
+ | Option | Default | Use it when |
56
+ |---|---|---|
57
+ | `--wcag 2.0\|2.1\|2.2` | `2.2` | The user names a WCAG version. |
58
+ | `--level A\|AA\|AAA` | `AA` | The user names a level. IBM Equal Access has no AAA rules, so it runs its AA rules and says so. |
59
+ | `--engine axe,ibm` | both | The user wants one rule engine. |
60
+ | `--tiers rules,interactions,vsr` | all three | The user wants fewer checks. |
61
+ | `--archetypes a,b` | all | The user cares about some components. Choose from button, link, dialog, menu, tabs, combobox, form-field, accordion, tooltip, chart. |
62
+ | `--lib-a11y on,off` | both | Only for libraries with opt-in accessibility features. |
63
+ | `--mapping <file>` | none | You wrote or edited a mapping file. |
64
+ | `--max-stories <n>` | 200 | A large Storybook. |
65
+ | `--out <dir>` | `./a11y-report` | The user names a folder. |
66
+ | `--plan` | off | You want to see how targets classify without running anything. |
67
+ | `--fail-on-axe <impact>` | off | CI-style gating on axe impact (`minor`, `moderate`, `serious`, `critical`). |
68
+ | `--fail-on-ibm <1\|2\|3>` | off | Gating on IBM Toolkit level. |
69
+ | `--fail-mode any\|all` | `any` | How to combine the two fail flags. |
70
+
71
+ Use the target and the settings the user already gave, and ask only for what's missing. If the user doesn't name a target to test (a URL, a Storybook, a local page or site, or an npm package), ask for one before you run anything. Don't pick a target for them.
72
+
73
+ Pass only the options in the table above, with the values it lists. If the user names a setting the tool doesn't have, or a value outside those lists (for example, WCAG 3.0), tell them which setting is unsupported, list the supported values, and ask which to use. Don't substitute a default or invent a value.
74
+
75
+ Show the user the exact command before you run it. If a target is ambiguous, ask one question, then go on.
76
+
77
+ Don't set the fail flags unless the user asks for gating. They change the exit code. They don't change the results.
78
+
79
+ ## 4. npm packages need fixtures
80
+
81
+ A package's components can't be guessed from its name. The first run installs the package, loads it in the browser, finds its exports (or custom elements), and writes a candidate mapping to `<out>/mapping.json`. It tests `button` and `link` from a template. Every other archetype needs a **fixture**, a small file that assembles the component the way the library intends.
82
+
83
+ 1. Run the audit once. Read `<out>/mapping.json` and the report's **Archetypes** table.
84
+ 2. Treat the mapping as a guess. Check each `export` or `tag` against what the user asked about.
85
+ 3. For each archetype marked `needs-fixture` that matters to the request, write `fixtures/<target id>/<archetype>.jsx` (`.js` for web components) in the working directory. Follow the contract and the examples below. `references/fixtures.md` has more: the mapping file, states, and library accessibility options. It should sit next to this file. If you can't open it, run `npx --yes automatica11y@latest guide fixtures` to print it. The contract here is enough for the first fixture.
86
+ 4. Run the same command again. The tool finds fixtures in that folder without `--mapping`.
87
+ 5. Don't invent fixtures for archetypes the user didn't ask about. A gap is an honest result.
88
+
89
+ **The fixture contract, in brief.**
90
+
91
+ - The default export renders the archetype in its starting state. For web components, the default export is the function `mount(container)`.
92
+ - Mark **exactly one** element `data-a11y-trigger`. It's what a person would focus and activate. If nothing can be activated, as with a chart, put both attributes on the same outermost element.
93
+ - Mark the main surface `data-a11y-root`, on the element that carries the role (`dialog`, `menu`, `tooltip`), not on an overlay or portal wrapper. It may appear only after the trigger fires, and it may render in a portal.
94
+ - Mount without console errors. Don't import CSS.
95
+ - The attributes have to reach the DOM. If a component drops unknown props, use the library's documented way to render your own element in its place (such as `asChild` in Radix, or the `as` prop in Headless UI), and mark that element. If the library has none, leave the archetype as a gap. Don't wrap the library's component in an element you add and mark that.
96
+
97
+ A React fixture. JSX works without importing React. Import the library from its package name, and the tool installs it:
98
+
99
+ ```jsx
100
+ import * as Dialog from "@radix-ui/react-dialog";
101
+
102
+ export default function Fixture() {
103
+ return (
104
+ <Dialog.Root>
105
+ <Dialog.Trigger data-a11y-trigger>Open dialog</Dialog.Trigger>
106
+ <Dialog.Portal>
107
+ <Dialog.Overlay />
108
+ <Dialog.Content data-a11y-root>
109
+ <Dialog.Title>Edit profile</Dialog.Title>
110
+ <Dialog.Description>Update your details.</Dialog.Description>
111
+ <Dialog.Close>Close</Dialog.Close>
112
+ </Dialog.Content>
113
+ </Dialog.Portal>
114
+ </Dialog.Root>
115
+ );
116
+ }
117
+ ```
118
+
119
+ A web component fixture. The tool imports the package first, so its elements are defined before `mount` runs. The `data-a11y-trigger` and `data-a11y-root` attributes can sit on a host, a slotted child, or an element inside an open shadow root. A closed shadow root hides its content from every tool:
120
+
121
+ ```js
122
+ export default function mount(container) {
123
+ container.innerHTML = `
124
+ <my-dialog>
125
+ <button slot="trigger" data-a11y-trigger type="button">Open</button>
126
+ <div slot="content" data-a11y-root role="dialog" aria-label="Details">...</div>
127
+ </my-dialog>`;
128
+ }
129
+ ```
130
+
131
+ Fixtures are code that the tool bundles and runs in a browser on the user's machine. Write them from the library's public documentation. If the documentation doesn't cover the API you need, don't guess, and don't stand in a plain element for the library's component, which would test the element and not the library. Leave the archetype as a gap and tell the user what you couldn't find. Tell the user the files exist and where. Package installs run with scripts turned off.
132
+
133
+ If the user asked for a comparison, give every target the same archetypes.
134
+
135
+ ## 5. Run, then read the results
136
+
137
+ Run the command. Note the exit code:
138
+
139
+ | Code | Meaning |
140
+ |---|---|
141
+ | 0 | The run completed. Findings don't change this unless a fail flag was set. |
142
+ | 1 | The run completed and a fail flag tripped. |
143
+ | 2 | The command was wrong. Read the message, fix it, and run again. |
144
+ | 3 | An environment problem. Relay the fix. |
145
+ | 4 | No target produced results. Say why, from `results.json`. |
146
+
147
+ Then read `<out>/results.json`. It's the source for everything you write. Read `<out>/report.md` too. It's a complete report the tool wrote on its own.
148
+
149
+ ## 6. Write the report
150
+
151
+ Write the narrative from `results.json`. Never write from memory, and never repeat a number you didn't read.
152
+
153
+ 1. Put the run date and tool versions first. Say that results are a snapshot.
154
+ 2. For a comparison, say that every target used the same archetypes, WCAG version, level, and rules.
155
+ 3. Open with the coverage matrix (target by archetype by tier). Then give the findings.
156
+ 4. Keep violations, needs-review items, and passes in separate lists. Never merge them.
157
+ 5. Break findings out by archetype and by impact.
158
+ 6. Use "no automated violations found" only for an engine that reported none for that target. For an engine that reported violations, list them as the tool reports them. Never say "accessible," "compliant," or "passes WCAG."
159
+ 7. Don't print a single score. If someone insists, pair any number with the coverage matrix and the automated-coverage caveat.
160
+ 8. Label virtual screen reader output **simulated**. Label library accessibility options **on** or **off** on every result that has one.
161
+ 9. Treat a gap, a not-testable result, an error, or a failed target as a finding. It never counts as a pass.
162
+ 10. If the comparison mixes component targets and page targets, open with a warning that the evidence isn't equivalent.
163
+ 11. Label every rule finding with its engine. Report axe-core and IBM Equal Access separately. Never add their counts together. Impact is axe-core's own label. IBM Toolkit level is IBM's staged adoption scale (1 is essential, high-impact requirements). Don't convert one into the other.
164
+ 12. End with a plain method note. Say what automated tools can't catch: whether alt text is meaningful, whether link and heading text make sense in context, cognitive load, real focus and reading order in use, and how real screen readers behave. Those need a person.
165
+
166
+ Use the structure of `report.md`. Quote selectors and rule IDs exactly as `results.json` has them.
167
+
168
+ ## 7. When a target can't be tested
169
+
170
+ Say these things plainly. Don't soften them, and don't fill in a result.
171
+
172
+ - **Unsupported framework.** The package needs a framework other than React or web components. Name it. v1 covers React and web components.
173
+ - **Not applicable.** The package has no rendering surface, such as a utility library. There's nothing to test.
174
+ - **Not testable.** The content is a canvas with no alternative, or sits in a closed shadow root. The rule engines can't see it, so the result is untested, not clean. The virtual screen reader also can't read open shadow roots.
175
+ - **Gap.** The archetype has no usable fixture or no matching export. Say what the archetype needs.
176
+ - **Error.** An interaction check couldn't finish. It's untested, not failed.
177
+ - **Failed target.** The target can't be reached, isn't a web page, or couldn't be built. The tool records it as failed with a reason. Tell the user which target failed, using the reason from `results.json`. Don't retry with guesses. If every target failed (exit code 4), stop. If others ran, report them, and list the failed target as a gap in coverage.
178
+
179
+ ## 8. Stay out of setup
180
+
181
+ Anything about installing, configuring, or repairing tools belongs to the tool. If `doctor` doesn't cover it, tell the user what failed and stop.
@@ -0,0 +1,59 @@
1
+ # Writing fixtures
2
+
3
+ A fixture renders one archetype (a dialog, a set of tabs, a menu) in its starting state, so the tool can test it. You write one when the tool can't build the component from a template. The tool only fills in `button` and `link`.
4
+
5
+ Write fixtures from the library's public documentation. Don't copy from memory when you aren't sure of the API. If the documentation doesn't cover the API you need, or you can't read it, don't guess. Leave the archetype without a fixture, so the report lists it as a gap, and tell the user what you couldn't find. Don't stand in a plain element for the library's component. That would test the plain element, and the result would say nothing about the library.
6
+
7
+ ## Where fixtures go
8
+
9
+ `fixtures/<target id>/<archetype>.jsx` for React, in the working folder: the folder where you run `automatica11y audit` or `automatica11y compare`. Use `.js` for web components. The target id is the label (`radix=npm:@radix-ui/react-dialog` has the id `radix`), or the package name with `/` turned into `-`, such as `radix-ui-react-dialog`. The report's **Targets** list shows each id.
10
+
11
+ To keep a fixture somewhere else, name it in a mapping file and pass `--mapping`:
12
+
13
+ ```json
14
+ {
15
+ "radix": {
16
+ "dialog": { "fixture": "my-fixtures/radix-dialog.jsx" },
17
+ "button": { "export": "Button" }
18
+ }
19
+ }
20
+ ```
21
+
22
+ The keys are target ids, then archetypes. `export` (React) or `tag` (web components) changes which export or element a template uses.
23
+
24
+ ## The contract
25
+
26
+ 1. The file's default export renders the archetype in its **initial state**.
27
+ 2. Mark **exactly one** element with `data-a11y-trigger`. This is what a person would focus and activate: the button that opens a dialog, the first tab, the combobox input, the form control. If nothing in the archetype can be activated, as with a chart, put `data-a11y-trigger` and `data-a11y-root` on the same element: the outermost one the library renders.
28
+ 3. Mark the **primary surface** with `data-a11y-root`. Put it on the element that carries the role (`role="dialog"`, `role="menu"`, `role="tooltip"`), not on an overlay or a portal wrapper. It's fine if the root doesn't exist until the trigger fires, and fine if it renders in a portal. The tool looks in the whole document.
29
+ 4. Mount without console errors. The tool treats errors as a broken fixture.
30
+ 5. Don't import CSS. The tool doesn't link it.
31
+ 6. Include every part the library's documentation marks as required (for example, a dialog's title and description), so the component doesn't log warnings.
32
+
33
+ The attributes have to reach the DOM. Pass `data-a11y-trigger` to the component that renders the real element. If a component drops unknown props, use the library's documented way to render your own element in its place (for example, `asChild` in Radix, or the `as` prop in Headless UI), and put the attribute on that native element. If the library has no such way, leave the archetype as a gap. Don't wrap the library's component in an element you add and mark that element, because the fixture would then test your element and not the library.
34
+
35
+ ## Examples
36
+
37
+ A React example and a web component example are in `SKILL.md`, in the section on npm packages. They follow the contract above.
38
+
39
+ For React, JSX works without importing React. Import the library from its package name. The tool installs it for you.
40
+
41
+ For web components, make `mount(container)` the file's default export. It adds the archetype to the container. The tool imports the package first, so its elements are defined before `mount` runs. The `data-a11y-trigger` and `data-a11y-root` attributes can sit on a host element, a slotted child, or an element inside an **open** shadow root. A **closed** shadow root hides its content from every tool, so the report lists it as not testable.
42
+
43
+ ## Library accessibility options
44
+
45
+ Some libraries ship accessibility features you have to switch on (a chart library's accessibility module, for example). Declare it at the archetype level in the mapping file, next to `fixture` or `export`, for example `{ "charts": { "chart": { "libA11y": true } } }`. The first key is the target id, and the second is the archetype. The fixture then receives a `libA11y` value, `true` or `false`. In React it's a prop. In web components it's a second argument, `mount(container, { libA11y })`. The tool runs the fixture both ways with `--lib-a11y on,off` and labels each result.
46
+
47
+ ```jsx
48
+ export default function Fixture({ libA11y }) {
49
+ return <Chart data-a11y-trigger data-a11y-root accessibility={libA11y} />;
50
+ }
51
+ ```
52
+
53
+ ## States
54
+
55
+ For a dialog, menu, tooltip, or combobox, the tool tests the closed state, then activates the trigger and tests the open state. For an accordion it tests collapsed, then expanded. The tool activates the trigger by clicking it. For a tooltip it focuses the trigger instead, so a tooltip has to open on focus. If it opens only on pointer hover, the open state never appears. Make sure activating the trigger really opens the surface, and that the element with `data-a11y-root` is then attached to the document, isn't `display: none` or `hidden`, and isn't `visibility: hidden`. The tool also counts the surface as open if the trigger has `aria-expanded="true"`. If neither holds, the open state is reported as failed, with the reason. The interaction checks also press keys and hover, but they don't change how the open state is reached.
56
+
57
+ ## Check your fixture
58
+
59
+ Run the audit and look at the report's **Archetypes** table. `ran` means it worked. A `gap` row says what went wrong, such as "didn't render an element with data-a11y-trigger" or "didn't bundle." Fix the file and run again.
package/src/cli.js ADDED
@@ -0,0 +1,49 @@
1
+ import { auditCommand } from "./commands/audit.js";
2
+ import { compareCommand } from "./commands/compare.js";
3
+ import { EXIT, UsageError, runSavedPlan, usage } from "./commands/common.js";
4
+ import { doctorCommand } from "./commands/doctor.js";
5
+ import { guideCommand } from "./commands/guide.js";
6
+ import { ownVersion } from "./env/versions.js";
7
+
8
+ const COMMANDS = {
9
+ audit: auditCommand,
10
+ compare: compareCommand,
11
+ run: runSavedPlan,
12
+ doctor: doctorCommand,
13
+ guide: guideCommand,
14
+ };
15
+
16
+ /**
17
+ * Run the CLI and return its exit code. Takes its streams and environment as arguments so tests can drive it.
18
+ * @param {string[]} argv Arguments after the program name.
19
+ * @param {Partial<import("./commands/common.js").Io>} [overrides]
20
+ * @returns {Promise<number>}
21
+ */
22
+ export async function main(argv, overrides = {}) {
23
+ /** @type {import("./commands/common.js").Io} */
24
+ const io = { stdout: process.stdout, stderr: process.stderr, cwd: process.cwd(), env: process.env, ...overrides };
25
+ const [first, ...rest] = argv;
26
+
27
+ if (first === "--version" || first === "-v") {
28
+ io.stdout.write(`${ownVersion()}\n`);
29
+ return EXIT.OK;
30
+ }
31
+ if (first === "--help" || first === "-h") {
32
+ io.stdout.write(usage());
33
+ return EXIT.OK;
34
+ }
35
+ const handler = first ? COMMANDS[/** @type {keyof typeof COMMANDS} */ (first)] : undefined;
36
+ if (!handler) {
37
+ io.stderr.write(first ? `Unknown command "${first}".\n\n${usage()}` : usage());
38
+ return EXIT.USAGE;
39
+ }
40
+ try {
41
+ return await handler(rest, io);
42
+ } catch (error) {
43
+ if (error instanceof UsageError) {
44
+ io.stderr.write(`${error.message}\n\nRun "automatica11y ${first} --help" for options.\n`);
45
+ return EXIT.USAGE;
46
+ }
47
+ throw error;
48
+ }
49
+ }
@@ -0,0 +1,4 @@
1
+ import { runCommand } from "./common.js";
2
+
3
+ /** @param {string[]} argv @param {import("./common.js").Io} io */
4
+ export const auditCommand = (argv, io) => runCommand("audit", argv, io);