automatica11y 0.3.3 → 0.4.1
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/AGENTS.md +2 -0
- package/README.md +39 -11
- package/package.json +5 -3
- package/skills/automatica11y/SKILL.md +3 -1
- package/skills/automatica11y-runner/SKILL.md +47 -14
- package/skills/automatica11y-runner/references/fixtures.md +38 -2
- package/src/commands/common.js +5 -2
- package/src/frameworks/index.js +42 -0
- package/src/frameworks/react.js +77 -0
- package/src/frameworks/vue.js +90 -0
- package/src/frameworks/wc.js +55 -0
- package/src/globals.d.ts +1 -0
- package/src/harness/bundle.js +7 -6
- package/src/harness/generate/dialects.js +64 -0
- package/src/harness/generate/index.js +12 -0
- package/src/harness/generate/jsx-recipes.js +224 -0
- package/src/harness/generate/jsx.js +76 -0
- package/src/harness/generate/kit.js +50 -0
- package/src/harness/generate/marking.js +64 -0
- package/src/harness/generate/probe.js +97 -0
- package/src/harness/generate/shared.js +12 -0
- package/src/harness/generate/wc-recipes.js +132 -0
- package/src/harness/npm-install.js +38 -7
- package/src/harness/settle.js +17 -0
- package/src/harness/storybook.js +1 -0
- package/src/harness/url.js +13 -3
- package/src/plan/classify.js +11 -4
- package/src/plan/mapping.js +10 -7
- package/src/plan/resolve-npm.js +15 -9
- package/src/plan/subpath.js +133 -0
- package/src/report/comparison.js +14 -9
- package/src/report/parts.js +36 -12
- package/src/run/audit-npm.js +118 -28
- package/src/run/generate-fixture.js +74 -0
- package/src/run/run-plan.js +19 -4
- package/src/run/summary.js +5 -0
- package/src/schema.js +30 -6
- package/src/tiers/computed/checks.js +44 -5
- package/src/tiers/computed/color.js +8 -0
- package/src/tiers/computed/index.js +2 -2
- package/src/tiers/computed/measure-kit.js +30 -10
- package/src/tiers/conditions/checks.js +283 -0
- package/src/tiers/conditions/index.js +30 -0
- package/src/tiers/conditions/kit.js +133 -0
- package/src/tiers/interactions/archetypes.js +111 -0
- package/src/tiers/interactions/helpers.js +56 -0
- package/src/tiers/interactions/index.js +16 -3
- package/src/harness/npm-react.js +0 -39
- package/src/harness/npm-wc.js +0 -30
package/AGENTS.md
CHANGED
|
@@ -10,6 +10,8 @@ Keep the user's request as they gave it, including the target and any settings t
|
|
|
10
10
|
npx --yes automatica11y@latest guide skill
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
If `automatica11y` is already on the `PATH` (the user ran `npm i -g automatica11y`, and `automatica11y --version` prints a version), you can run `automatica11y guide skill` and `automatica11y guide fixtures` instead, and use `automatica11y` wherever the steps say `npx --yes automatica11y@latest`. The version check in the steps still applies. An installed copy can be older than the steps expect, and then the check stops you. Use `npx` when the installed version is the wrong series, or when nothing is installed.
|
|
14
|
+
|
|
13
15
|
When the steps tell you to write a fixture, read the fixture guide:
|
|
14
16
|
|
|
15
17
|
```bash
|
package/README.md
CHANGED
|
@@ -13,12 +13,22 @@ It's **not** an attestation or certification tool. Automated checks cover only p
|
|
|
13
13
|
|
|
14
14
|
You need Node 20 or newer and Chrome or Chromium.
|
|
15
15
|
|
|
16
|
+
Install it once, and `automatica11y` is on your `PATH`, with the short name `a11y` too:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm i -g automatica11y
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Then run it without `npx`:
|
|
23
|
+
|
|
16
24
|
```bash
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
automatica11y doctor
|
|
26
|
+
automatica11y audit https://example.com
|
|
27
|
+
automatica11y compare radix=npm:@radix-ui/react-dialog aria=npm:react-aria-components
|
|
20
28
|
```
|
|
21
29
|
|
|
30
|
+
You can skip the install and put `npx` in front instead, for example `npx automatica11y doctor`. `npx` fetches the tool the first time and checks the registry after that. A global install doesn't update itself, so run `npm i -g automatica11y@latest` to upgrade, and `automatica11y --version` to see what you have. The rest of this page writes the short form. Add `npx` if you didn't install it.
|
|
31
|
+
|
|
22
32
|
`doctor` checks your setup. If it can't find a browser, it prints the command that installs one:
|
|
23
33
|
|
|
24
34
|
```bash
|
|
@@ -36,7 +46,7 @@ A target is `[label=]<spec>`. The label is optional, and names the target in the
|
|
|
36
46
|
| A live page | `https://example.com/page` |
|
|
37
47
|
| 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
48
|
| 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. |
|
|
49
|
+
| An npm package | `npm:name`, `npm:@scope/name`, or `npm:name@version`. Add a sub-path to test one entry of a package: `npm:@scope/pkg/button` or `npm:@scope/pkg@1.2.3/button/v2`. React, Vue 3, and web component libraries work. |
|
|
40
50
|
|
|
41
51
|
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
52
|
|
|
@@ -44,11 +54,21 @@ A local `.html` file is served over `http://localhost`, never `file://`. A stati
|
|
|
44
54
|
|
|
45
55
|
## What it checks.
|
|
46
56
|
|
|
47
|
-
|
|
57
|
+
Five tiers run by default. Use `--tiers` to pick fewer.
|
|
48
58
|
|
|
49
59
|
- **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
|
|
60
|
+
- **Interactions.** Keyboard and focus checks for ten archetypes: button, link, dialog, menu, tabs, combobox, form-field, accordion, tooltip, and live-region (a message that appears or changes without moving focus, such as an alert or status message). Each check runs on a fresh page.
|
|
51
61
|
- **Computed checks.** automatica11y's own measurements from resolved styles in the browser, for the trigger of each archetype fixture: text contrast in rest, hover, keyboard focus, and pressed states (1.4.3), the contrast of the control's edge, fill, or icon (1.4.11), and the contrast and thickness of the focus indicator (1.4.11, and 2.4.13 at level AAA). A page that uses a gradient, an image, or transparency behind the control can't be reduced to one color, so that check reports `undetermined`, which is a gap and never a pass. These results are reported on their own and never added to the rule engines' counts.
|
|
62
|
+
- **Conditions.** How the page holds up under a user's settings and environment. Each check opens fresh copies of the page and says what happened:
|
|
63
|
+
- `prefers-reduced-motion: reduce` (2.3.3, 2.2.2): do animations that move or repeat stop or go away? Motion driven by JavaScript timers isn't visible to this check.
|
|
64
|
+
- `prefers-color-scheme: dark` (1.4.3): if the page changes in dark mode, does every piece of text keep its contrast? A page that doesn't adapt isn't failed.
|
|
65
|
+
- `prefers-contrast: more` (1.4.3, 1.4.6): if the page responds, does the lowest text contrast stay above the minimum and not drop? The check says whether enhanced contrast (7:1) is reached. `prefers-contrast: less` (1.4.3): if the page softens, does text stay above the minimum? A page that doesn't respond isn't failed.
|
|
66
|
+
- `prefers-reduced-transparency: reduce` (1.4.3, 1.4.11): do surfaces that hold text stop being see-through (translucent backgrounds, backdrop blur)? This preference isn't a WCAG requirement. It matters because see-through backgrounds make text contrast unpredictable.
|
|
67
|
+
- Forced colors (1.4.11, 2.4.7): is the focus indicator still visible? A ring drawn with `box-shadow` disappears in forced colors, so use an outline.
|
|
68
|
+
- Reflow at 320 CSS pixels (1.4.10): does the page scroll sideways, or does anything reach past the right edge? Two-dimensional content such as data tables and maps is exempt, so a person judges those.
|
|
69
|
+
- Text spacing (1.4.12): with the spacing the criterion names, does any element cut off its text? Overlapping text isn't checked.
|
|
70
|
+
|
|
71
|
+
Conditions run on whole pages and on component fixtures. Storybook stories are reported as not applicable.
|
|
52
72
|
- **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.
|
|
53
73
|
|
|
54
74
|
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.
|
|
@@ -60,10 +80,11 @@ Every result says what it ran, or why it didn't. A gap, a failure, or a result t
|
|
|
60
80
|
| `--wcag 2.0\|2.1\|2.2` | `2.2` | The WCAG version. |
|
|
61
81
|
| `--level A\|AA\|AAA` | `AA` | The conformance level. IBM Equal Access has no AAA rules, so it runs its AA rules and says so. |
|
|
62
82
|
| `--engine axe,ibm` | both | Which rule engines run. |
|
|
63
|
-
| `--tiers rules,interactions,computed,vsr` | all | Which tiers run. |
|
|
83
|
+
| `--tiers rules,interactions,computed,conditions,vsr` | all | Which tiers run. |
|
|
64
84
|
| `--archetypes a,b` | all | Limit npm and Storybook targets to these archetypes. |
|
|
65
85
|
| `--lib-a11y on,off` | both | For libraries with opt-in accessibility features. See [the fixture guide](skills/automatica11y-runner/references/fixtures.md). |
|
|
66
86
|
| `--mapping <file>` | none | A mapping file for npm targets. |
|
|
87
|
+
| `--no-generate` | off | Don't build fixtures for npm packages. Only authored fixtures and the `button` and `link` templates run. |
|
|
67
88
|
| `--max-stories <n>` | `200` | The Storybook story cap. The cap spreads over components. |
|
|
68
89
|
| `--out <dir>` | `./a11y-report` | Where results go. |
|
|
69
90
|
| `--plan` | off | Classify the targets and write `plan.json`, then stop. Nothing installs and no browser launches. |
|
|
@@ -96,13 +117,20 @@ One failing target doesn't stop a comparison. It's recorded with its reason, and
|
|
|
96
117
|
|
|
97
118
|
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`.
|
|
98
119
|
|
|
99
|
-
|
|
120
|
+
Each archetype's fixture comes from the first of these that applies:
|
|
121
|
+
|
|
122
|
+
1. **Authored.** A file you or your agent write at `fixtures/<target id>/<archetype>.jsx` (`.js` for web components). The same `.jsx` works for React and for Vue 3, following [the fixture guide](skills/automatica11y-runner/references/fixtures.md). It always wins.
|
|
123
|
+
2. **Template.** The tool builds `button` and `link` tests from the export name alone.
|
|
124
|
+
3. **Generated.** For dialog, menu, tooltip, tabs, accordion, combobox, form-field, and live-region, the tool builds candidate fixtures from what the package exports. It looks for compound parts by common names (a root, a trigger, a content part, a title, a close part, and so on), either as `Dialog.Root` or as `DialogRoot`, and it tries the usual ways of switching a component on (an `open` prop and a close handler). For web components it reads what each element says about itself: its observed attributes, its members, and its slots. It then bundles each candidate, loads it, and checks that exactly one element is the trigger, that nothing logged an error, that activating the trigger shows a root, and that the root carries a role that fits. The first candidate that passes is used. If none does, the archetype is a gap, and the report lists what was tried and why each attempt failed.
|
|
125
|
+
4. **Gap.** Anything else is a gap in the report.
|
|
126
|
+
|
|
127
|
+
A generated fixture is a guess about how the library is meant to be assembled, so a failure may come from how it was wired and not from the library. Reports mark generated results and treat them as lower evidence than an authored fixture. The source of each generated fixture is written to `generated/<target id>/<archetype>.jsx` beside the report. Copy one to `fixtures/<target id>/` and edit it to make it an authored fixture. Use `--no-generate` to turn generation off, for example when you want a strict comparison of authored fixtures only.
|
|
100
128
|
|
|
101
129
|
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.
|
|
102
130
|
|
|
103
131
|
## Use it with an AI agent.
|
|
104
132
|
|
|
105
|
-
The tool prints its own guidance, so any agent that can run `npx` can learn to use it
|
|
133
|
+
The tool prints its own guidance, so any agent that can run `npx` can learn to use it. If the tool is installed globally, the agent can run `automatica11y guide skill` instead of `npx automatica11y@latest guide skill`:
|
|
106
134
|
|
|
107
135
|
```bash
|
|
108
136
|
npx automatica11y@latest guide # where to start (the AGENTS.md file)
|
|
@@ -119,9 +147,9 @@ The guidance ships with the tool, so it always matches the version you run. Tell
|
|
|
119
147
|
## Limits.
|
|
120
148
|
|
|
121
149
|
- 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.
|
|
122
|
-
- Components are tested in the states a fixture shows. Dialogs, menus, tooltips, and comboboxes run closed and open. Other states aren't visited.
|
|
150
|
+
- Components are tested in the states a fixture shows. Dialogs, menus, tooltips, and comboboxes run closed and open, and live regions run before and after the message. Other states aren't visited.
|
|
123
151
|
- 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.
|
|
124
|
-
-
|
|
152
|
+
- Svelte, Angular, Vue 2, and other frameworks report "unsupported framework." Their Storybooks work, because a Storybook renders stories in a page whatever the framework.
|
|
125
153
|
- Native screen readers aren't part of this version.
|
|
126
154
|
- Results are a snapshot. The tools run at their latest versions, and the report records them.
|
|
127
155
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "automatica11y",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Test and compare the accessibility of web pages, Storybook builds, and npm component libraries.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
-
"automatica11y": "bin/automatica11y.js"
|
|
8
|
+
"automatica11y": "bin/automatica11y.js",
|
|
9
|
+
"a11y": "bin/automatica11y.js"
|
|
9
10
|
},
|
|
10
11
|
"files": [
|
|
11
12
|
"bin",
|
|
@@ -35,7 +36,8 @@
|
|
|
35
36
|
"@types/node": "^26.6.4",
|
|
36
37
|
"react": "^19.3.0",
|
|
37
38
|
"react-dom": "^19.3.0",
|
|
38
|
-
"typescript": "^7.0.2"
|
|
39
|
+
"typescript": "^7.0.2",
|
|
40
|
+
"vue": "^3.5.43"
|
|
39
41
|
},
|
|
40
42
|
"publishConfig": {
|
|
41
43
|
"access": "public"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: automatica11y
|
|
3
|
-
compatibility: Needs Node 20 or newer, a shell that can run npx, and network access to the npm registry.
|
|
3
|
+
compatibility: Needs Node 20 or newer, a shell that can run npx or the installed automatica11y command, and network access to the npm registry.
|
|
4
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
5
|
---
|
|
6
6
|
|
|
@@ -20,6 +20,8 @@ automatica11y tests and compares web accessibility. This skill only gets you sta
|
|
|
20
20
|
|
|
21
21
|
If the command exits with an error or prints no usable output, including after that retry, tell the user it failed, include the error text, and stop. Don't continue with partial instructions.
|
|
22
22
|
|
|
23
|
+
If `automatica11y` is already installed (`automatica11y --version` prints a version), you can run `automatica11y guide skill` instead. The steps check that the installed version matches them.
|
|
24
|
+
|
|
23
25
|
After reading the steps, 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 steps, and ask which to use. Don't ask again for anything they've already said.
|
|
24
26
|
|
|
25
27
|
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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
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.
|
|
3
|
+
compatibility: Needs Node 20 or newer, Chrome or Chromium, a shell that can run npx or the installed automatica11y command, and network access to the npm registry.
|
|
4
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
5
|
---
|
|
6
6
|
|
|
@@ -16,13 +16,15 @@ The tool is **not** an attestation or certification tool. Automated checks cover
|
|
|
16
16
|
|
|
17
17
|
Do sections 1 and 2 before you run an audit. A question to the user about a missing target or an unsupported setting (section 3) can come before or after them.
|
|
18
18
|
|
|
19
|
-
This skill works with automatica11y **0.
|
|
19
|
+
This skill works with automatica11y **0.4.x**. Run:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
22
|
npx --yes automatica11y@latest --version
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
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.
|
|
25
|
+
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.4.`, stop. Tell the user the version you got and the series this copy expects (`0.4.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.
|
|
26
|
+
|
|
27
|
+
If the user installed the tool globally (`npm i -g automatica11y`), `automatica11y` is on the `PATH` (so is the short name `a11y`), and it runs the same commands as `npx --yes automatica11y@latest`. Every command in this skill is written with `npx`, so replace that prefix with `automatica11y` only when `automatica11y --version` passes the check above. If the installed version is the wrong series, or nothing is installed, use `npx`.
|
|
26
28
|
|
|
27
29
|
## 2. Check the setup
|
|
28
30
|
|
|
@@ -48,7 +50,7 @@ Use `audit` for one target and `compare` for two or more, even when they're diff
|
|
|
48
50
|
| A live page | `https://example.com/page` |
|
|
49
51
|
| A local page or site | `./page.html` or `./dist`. A path with no prefix is relative to the working folder. |
|
|
50
52
|
| A Storybook | its URL, or a local folder with `index.json` |
|
|
51
|
-
| An npm package | `npm:name`, `npm:@scope/name`, or `npm:name@version` |
|
|
53
|
+
| An npm package | `npm:name`, `npm:@scope/name`, or `npm:name@version`. To test one entry of a package, add a sub-path after the version: `npm:@scope/pkg/button` or `npm:@scope/pkg@1.2.3/button/v2`. The sub-path is what a person would import (`import ... from "@scope/pkg/button"`), and it has to be something the package exports. |
|
|
52
54
|
|
|
53
55
|
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.
|
|
54
56
|
|
|
@@ -59,10 +61,11 @@ Options you can set, and nothing else:
|
|
|
59
61
|
| `--wcag 2.0\|2.1\|2.2` | `2.2` | The user names a WCAG version. |
|
|
60
62
|
| `--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. |
|
|
61
63
|
| `--engine axe,ibm` | both | The user wants one rule engine. |
|
|
62
|
-
| `--tiers rules,interactions,computed,vsr` | all
|
|
63
|
-
| `--archetypes a,b` | all | The user cares about some components. Choose from button, link, dialog, menu, tabs, combobox, form-field, accordion, tooltip, chart. |
|
|
64
|
+
| `--tiers rules,interactions,computed,conditions,vsr` | all five | The user wants fewer checks. |
|
|
65
|
+
| `--archetypes a,b` | all | The user cares about some components. Choose from button, link, dialog, menu, tabs, combobox, form-field, accordion, tooltip, live-region, chart. |
|
|
64
66
|
| `--lib-a11y on,off` | both | Only for libraries with opt-in accessibility features. |
|
|
65
67
|
| `--mapping <file>` | none | You wrote or edited a mapping file. |
|
|
68
|
+
| `--no-generate` | off | The user wants only authored fixtures and the `button` and `link` templates, with no generated ones. |
|
|
66
69
|
| `--max-stories <n>` | 200 | A large Storybook. |
|
|
67
70
|
| `--out <dir>` | `./a11y-report` | The user names a folder. |
|
|
68
71
|
| `--plan` | off | You want to see how targets classify without running anything. |
|
|
@@ -80,20 +83,26 @@ Don't set the fail flags unless the user asks for gating. They change the exit c
|
|
|
80
83
|
|
|
81
84
|
## 4. npm packages need fixtures
|
|
82
85
|
|
|
83
|
-
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
|
|
86
|
+
A package's components can't be guessed from its name alone. 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 then fills each archetype from the first source that applies:
|
|
87
|
+
|
|
88
|
+
- **Authored.** A **fixture** you write: a small file that assembles the component the way the library intends. It always wins.
|
|
89
|
+
- **Template.** The tool builds `button` and `link` from the export name.
|
|
90
|
+
- **Generated.** For dialog, menu, tooltip, tabs, accordion, combobox, form-field, and live-region, the tool builds candidates from the package's compound parts (or from what a custom element says about itself), runs each in the browser, and keeps the first that behaves: one trigger, no errors, a root that appears with a fitting role. The `--no-generate` option turns this off.
|
|
91
|
+
- **Gap.** If none of those worked, the report lists what was tried and why each attempt failed.
|
|
84
92
|
|
|
85
|
-
1. Run the audit once. Read `<out>/mapping.json` and the report's **Archetypes** table.
|
|
93
|
+
1. Run the audit once. Read `<out>/mapping.json` and the report's **Archetypes** table, which has a **Fixture** column.
|
|
86
94
|
2. Treat the mapping as a guess. Check each `export` or `tag` against what the user asked about.
|
|
87
|
-
3. Each archetype in `mapping.json` has a status. `needs-fixture` means the tool found the component but
|
|
95
|
+
3. Each archetype in `mapping.json` has a status. `generated` means the tool built a fixture that passed its checks, and the source is in `<out>/generated/`. `needs-fixture` means the tool found the component but couldn't build a working fixture. `no-match` means no export or custom element looks like that archetype. 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.
|
|
88
96
|
4. Run the same command again. The tool finds fixtures in that folder without `--mapping`.
|
|
89
|
-
5.
|
|
97
|
+
5. For an archetype that matters, read the generated source before you rely on it. If it looks wrong for the library, write an authored fixture from the documentation. To keep a generated one, copy it from `<out>/generated/<target id>/` into `fixtures/<target id>/`.
|
|
98
|
+
6. Don't invent fixtures for archetypes the user didn't ask about. For a `no-match` archetype, write a fixture only if the library's documentation names a component, or a documented way, to make it. Otherwise leave it as a gap. A gap is an honest result.
|
|
90
99
|
|
|
91
100
|
**The fixture contract, in brief.**
|
|
92
101
|
|
|
93
102
|
- The default export renders the archetype in its starting state. For web components, the default export is the function `mount(container)`.
|
|
94
103
|
- 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.
|
|
95
104
|
- 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.
|
|
96
|
-
- Mount without console errors.
|
|
105
|
+
- Mount without console errors. If the library's documentation says to load a stylesheet, import it. When it comes from another package, list that package under `install` in the mapping file (`references/fixtures.md` has the shape).
|
|
97
106
|
- 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.
|
|
98
107
|
|
|
99
108
|
A React fixture. JSX works without importing React. Import the library from its package name, and the tool installs it:
|
|
@@ -118,6 +127,28 @@ export default function Fixture() {
|
|
|
118
127
|
}
|
|
119
128
|
```
|
|
120
129
|
|
|
130
|
+
A Vue 3 fixture. It's also JSX, and a function default export is a functional component. The tool turns JSX into `h()` calls and supplies `h` and `Fragment`, so **don't import `h`**. Children become the default slot. A fixture that needs a plugin or global setup can also export `setup(app)`, which runs before the app mounts. Import the library from its package name, and the tool installs it along with Vue:
|
|
131
|
+
|
|
132
|
+
```jsx
|
|
133
|
+
import { DialogClose, DialogContent, DialogDescription, DialogOverlay, DialogPortal, DialogRoot, DialogTitle, DialogTrigger } from "reka-ui";
|
|
134
|
+
|
|
135
|
+
export default function Fixture() {
|
|
136
|
+
return (
|
|
137
|
+
<DialogRoot>
|
|
138
|
+
<DialogTrigger data-a11y-trigger>Open dialog</DialogTrigger>
|
|
139
|
+
<DialogPortal>
|
|
140
|
+
<DialogOverlay />
|
|
141
|
+
<DialogContent data-a11y-root>
|
|
142
|
+
<DialogTitle>Edit profile</DialogTitle>
|
|
143
|
+
<DialogDescription>Update your details.</DialogDescription>
|
|
144
|
+
<DialogClose>Close</DialogClose>
|
|
145
|
+
</DialogContent>
|
|
146
|
+
</DialogPortal>
|
|
147
|
+
</DialogRoot>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
121
152
|
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:
|
|
122
153
|
|
|
123
154
|
```js
|
|
@@ -164,8 +195,10 @@ Write the narrative from `results.json`. Never write from memory, and never repe
|
|
|
164
195
|
10. If the comparison mixes component targets and page targets, open with a warning that the evidence isn't equivalent.
|
|
165
196
|
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.
|
|
166
197
|
12. Report computed checks (contrast measured from resolved styles) in their own section. Give the measured ratio next to the ratio the criterion needs, and name the state or the method. Never add them to axe-core or IBM counts. Treat `undetermined` as a gap, never as a pass. When a control has visible text and a pale edge, the result is `not-applicable`, because the text identifies the control. Say that, and leave it for a person to confirm.
|
|
167
|
-
13.
|
|
168
|
-
14.
|
|
198
|
+
13. Report conditions checks (reduced motion, dark mode, more and less contrast, reduced transparency, forced colors, reflow at 320 pixels, text spacing) in their own section, with the numbers from `results.json`. Never add them to axe-core or IBM counts. "Not applicable" means the page doesn't use the feature, such as a page with no dark theme, so don't list it as a failure or a pass. Say what each check can't see: JavaScript-driven motion, overlapping text, and two-dimensional content that WCAG exempts from reflow.
|
|
199
|
+
14. Say which results come from generated fixtures. A generated fixture is a guess about how the library is assembled, so a failure may come from the wiring and not from the library. Call those results lower evidence than an authored fixture, name the recipe from the **Fixture** column, and don't compare a generated result with an authored one as if they were equal. If the user wants a result to stand on its own, write an authored fixture for it.
|
|
200
|
+
15. When you name a WCAG criterion, use the number and name exactly as `report.md` prints them, and keep its "WCAG data" credit to the W3C. Don't write criterion names or levels from memory.
|
|
201
|
+
16. 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.
|
|
169
202
|
|
|
170
203
|
Use the structure of `report.md`. Quote selectors and rule IDs exactly as `results.json` has them.
|
|
171
204
|
|
|
@@ -173,7 +206,7 @@ Use the structure of `report.md`. Quote selectors and rule IDs exactly as `resul
|
|
|
173
206
|
|
|
174
207
|
Say these things plainly. Don't soften them, and don't fill in a result.
|
|
175
208
|
|
|
176
|
-
- **Unsupported framework.** The package needs a framework other than React or web components. Name it.
|
|
209
|
+
- **Unsupported framework.** The package needs a framework other than React, Vue 3, or web components. Name it, and say which version is unsupported (Vue 2, for example). This version covers React, Vue 3, and web components. A Storybook for the library still works, whatever the framework.
|
|
177
210
|
- **Not applicable.** The package has no rendering surface, such as a utility library. There's nothing to test.
|
|
178
211
|
- **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.
|
|
179
212
|
- **Gap.** The archetype has no usable fixture or no matching export. Say what the archetype needs.
|
|
@@ -6,7 +6,7 @@ Write fixtures from the library's public documentation. Don't copy from memory w
|
|
|
6
6
|
|
|
7
7
|
## Where fixtures go
|
|
8
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.
|
|
9
|
+
`fixtures/<target id>/<archetype>.jsx` for React and Vue 3, 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
10
|
|
|
11
11
|
To keep a fixture somewhere else, name it in a mapping file and pass `--mapping`:
|
|
12
12
|
|
|
@@ -21,13 +21,19 @@ To keep a fixture somewhere else, name it in a mapping file and pass `--mapping`
|
|
|
21
21
|
|
|
22
22
|
The keys are target ids, then archetypes. `export` (React) or `tag` (web components) changes which export or element a template uses.
|
|
23
23
|
|
|
24
|
+
## Start from a generated fixture
|
|
25
|
+
|
|
26
|
+
You often don't need to write a fixture from nothing. For dialog, menu, tooltip, tabs, accordion, combobox, form-field, and live-region, the tool tries to build one from the package's exports and keeps it only if it works. The source is written to `<out>/generated/<target id>/<archetype>.jsx` (`.js` for web components). To make one an authored fixture, copy it to `fixtures/<target id>/` and edit it. It will carry a block of marking code at the top (`startMarking`). That code marks the trigger and root by role, because a generated fixture can't know how the library forwards props. In an authored fixture you can drop it and put `data-a11y-trigger` and `data-a11y-root` on the right elements yourself, which is clearer and doesn't depend on timing.
|
|
27
|
+
|
|
28
|
+
If the tool couldn't build a working fixture, the report's **Archetypes** table says what it tried and why each attempt failed. That's a good place to start reading.
|
|
29
|
+
|
|
24
30
|
## The contract
|
|
25
31
|
|
|
26
32
|
1. The file's default export renders the archetype in its **initial state**.
|
|
27
33
|
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
34
|
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
35
|
4. Mount without console errors. The tool treats errors as a broken fixture.
|
|
30
|
-
5.
|
|
36
|
+
5. A stylesheet the fixture imports is bundled and linked. If the library needs one (a token file, a theme) and its documentation says to load it, import it. If that stylesheet is in another package, list the package under `install` in the mapping (see below). Without the stylesheet the library renders unstyled, and the results describe that, not the library.
|
|
31
37
|
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
38
|
|
|
33
39
|
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.
|
|
@@ -38,6 +44,8 @@ A React example and a web component example are in `SKILL.md`, in the section on
|
|
|
38
44
|
|
|
39
45
|
For React, JSX works without importing React. Import the library from its package name. The tool installs it for you.
|
|
40
46
|
|
|
47
|
+
For Vue 3, a fixture is also JSX, and the default export is a component: a function that returns JSX is a functional component, or you can export a component object. The tool turns JSX into `h()` calls and supplies `h` and `Fragment` itself, so don't import them. Importing `h` fails the bundle with "symbol already declared". JSX children become the default slot. Vue's reactivity works, so `import { ref } from "vue"` for state, and wrap the JSX in `defineComponent({ setup() { ... return () => (<jsx/>) } })`. A library that needs a plugin, a theme, or global components can export a function named `setup(app)`, which runs before the app mounts, for example `export function setup(app) { app.use(plugin) }`. Named slots and `v-model` aren't JSX syntax. Pass a slots object as the children, or set `modelValue` and `onUpdate:modelValue` with a spread, for example `{...{ "onUpdate:open": (next) => (open.value = next) }}`.
|
|
48
|
+
|
|
41
49
|
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
50
|
|
|
43
51
|
## Library accessibility options
|
|
@@ -50,10 +58,38 @@ export default function Fixture({ libA11y }) {
|
|
|
50
58
|
}
|
|
51
59
|
```
|
|
52
60
|
|
|
61
|
+
## Companion packages
|
|
62
|
+
|
|
63
|
+
Some libraries need another package beside them, such as a design token stylesheet or a theme. Name it under `install` in the mapping file, next to `fixture`. The first key is the target id, and the second is the archetype. The tool installs it into the same folder as the library, with install scripts off, and the fixture can import it:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"lib": {
|
|
68
|
+
"live-region": { "fixture": "fixtures/lib/live-region.js", "install": ["@scope/tokens"] }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
import "@scope/tokens/style.css";
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
List only what the library's documentation asks you to load. Each entry has to be a package name with an optional version. Flags, paths, and URLs are refused.
|
|
78
|
+
|
|
53
79
|
## States
|
|
54
80
|
|
|
55
81
|
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
82
|
|
|
83
|
+
## Live regions
|
|
84
|
+
|
|
85
|
+
The `live-region` archetype covers any message that appears, changes, or goes away without moving focus: alerts, status messages, toasts, snackbars, and other `role="alert"`, `role="status"`, `role="log"`, or `aria-live` content. A library's alert component is a fixture for this archetype. The archetype isn't named after any one component.
|
|
86
|
+
|
|
87
|
+
- `data-a11y-trigger` goes on the control that makes the message appear or change, such as a "Save" button. It can't be the message itself. A message that's on the page from the start has nothing to announce, so the tool reports a gap and asks for a trigger.
|
|
88
|
+
- `data-a11y-root` goes on the message: the element that carries the role, or the element whose text changes. It may already be in the page and empty, or the trigger may insert it.
|
|
89
|
+
- If the message has a dismiss control, put it inside the root. The tool presses Enter on it and checks that the message goes away and that focus lands somewhere sensible.
|
|
90
|
+
- The tool checks that the message sits in a live region, that the region was in the page before the message arrived (except for `role="alert"`, which is announced on insertion), that the politeness fits the role, and that focus stays on the trigger.
|
|
91
|
+
- The states are "before message" and "message shown". The tool activates the trigger by clicking it, then waits for the message to have text.
|
|
92
|
+
|
|
57
93
|
## Check your fixture
|
|
58
94
|
|
|
59
95
|
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/commands/common.js
CHANGED
|
@@ -26,6 +26,7 @@ const OPTION_DEFS = /** @type {const} */ ({
|
|
|
26
26
|
"lib-a11y": { type: "string" },
|
|
27
27
|
mapping: { type: "string" },
|
|
28
28
|
plan: { type: "boolean" },
|
|
29
|
+
"no-generate": { type: "boolean" },
|
|
29
30
|
out: { type: "string" },
|
|
30
31
|
"max-stories": { type: "string" },
|
|
31
32
|
"fail-on-axe": { type: "string" },
|
|
@@ -110,6 +111,7 @@ export function parseRunArgs(command, argv) {
|
|
|
110
111
|
archetypes: /** @type {any} */ (archetypes),
|
|
111
112
|
mapping: values.mapping ?? null,
|
|
112
113
|
maxStories,
|
|
114
|
+
generate: !values["no-generate"],
|
|
113
115
|
out: values.out ?? "./a11y-report",
|
|
114
116
|
fail,
|
|
115
117
|
};
|
|
@@ -120,7 +122,7 @@ export function parseRunArgs(command, argv) {
|
|
|
120
122
|
export function describeTarget(target) {
|
|
121
123
|
if (target.status === "failed") return `failed: ${target.reason}`;
|
|
122
124
|
const r = target.resolved ?? {};
|
|
123
|
-
if (target.kind?.startsWith("npm")) return `${r.name}@${r.version ?? r.requested ?? "latest"}${r.framework ? ` (${r.framework})` : ""}`;
|
|
125
|
+
if (target.kind?.startsWith("npm")) return `${r.name}@${r.version ?? r.requested ?? "latest"}${r.subpath ? `/${r.subpath}` : ""}${r.framework ? ` (${r.framework})` : ""}`;
|
|
124
126
|
return r.url ?? r.path ?? "";
|
|
125
127
|
}
|
|
126
128
|
|
|
@@ -237,12 +239,13 @@ export async function runSavedPlan(argv, io) {
|
|
|
237
239
|
const FLAGS = `Options:
|
|
238
240
|
--wcag <2.0|2.1|2.2> WCAG version. Default 2.2.
|
|
239
241
|
--level <A|AA|AAA> Conformance level. Default AA.
|
|
240
|
-
--tiers <list> rules, interactions, computed, vsr. Default all
|
|
242
|
+
--tiers <list> rules, interactions, computed, conditions, vsr. Default all five.
|
|
241
243
|
--engine <list> axe, ibm. Default both.
|
|
242
244
|
--archetypes <list> Limit npm and Storybook targets to these archetypes.
|
|
243
245
|
--lib-a11y <list> on, off. Default both.
|
|
244
246
|
--mapping <file> Archetype mapping file.
|
|
245
247
|
--max-stories <n> Storybook story cap. Default 200.
|
|
248
|
+
--no-generate Don't build fixtures for npm packages. Only authored fixtures and the button and link templates run.
|
|
246
249
|
--plan Resolve and print the plan, then stop.
|
|
247
250
|
--out <dir> Output directory. Default ./a11y-report.
|
|
248
251
|
--fail-on-axe <impact> minor, moderate, serious, or critical.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The framework adapters. Each one says how to recognize a package built for its framework, install the framework's runtime,
|
|
3
|
+
* bundle and mount a fixture, list a package's exports, template the simple archetypes, generate the rest, and describe the
|
|
4
|
+
* result in a report. Everything after that (the browser, the tiers, the probe, the reports) knows nothing about a framework.
|
|
5
|
+
*
|
|
6
|
+
* To add a framework, write a module with the same shape as react.js, add it here, and add its id to FLAVORS and its kind to
|
|
7
|
+
* TARGET_KINDS in schema.js. A test checks that the two lists agree.
|
|
8
|
+
*
|
|
9
|
+
* @typedef {{
|
|
10
|
+
* id: string,
|
|
11
|
+
* label: string,
|
|
12
|
+
* kind: string,
|
|
13
|
+
* noun: string,
|
|
14
|
+
* extension: string,
|
|
15
|
+
* runtime: string[],
|
|
16
|
+
* detect: (meta: any) => { kind: string, framework: string, reason: string } | null,
|
|
17
|
+
* bundle: (workDir: string) => { alias: Record<string, string>, define?: Record<string, string>, esbuild: Record<string, any> },
|
|
18
|
+
* entry: (fixturePath: string, pkg: string) => string,
|
|
19
|
+
* discoverEntry: (pkg: string) => string,
|
|
20
|
+
* template: (archetype: string, pkg: string, name?: string) => string | null,
|
|
21
|
+
* generate: (input: any) => { candidates: Array<{ id: string, summary: string, source: string, used: string[] }>, reason: string | null },
|
|
22
|
+
* describe: (npm: any) => string,
|
|
23
|
+
* }} Adapter
|
|
24
|
+
*/
|
|
25
|
+
import react from "./react.js";
|
|
26
|
+
import vue from "./vue.js";
|
|
27
|
+
import wc from "./wc.js";
|
|
28
|
+
|
|
29
|
+
/** @type {Record<string, Adapter>} */
|
|
30
|
+
export const ADAPTERS = { react, vue, wc };
|
|
31
|
+
|
|
32
|
+
/** The adapter for a flavor (`react`, `vue`, or `wc`). */
|
|
33
|
+
export function adapterFor(id) {
|
|
34
|
+
const adapter = ADAPTERS[id];
|
|
35
|
+
if (!adapter) throw new Error(`There's no adapter for "${id}".`);
|
|
36
|
+
return adapter;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The adapter for a plan target's kind, such as `npm-vue`, or null for a kind no adapter owns. */
|
|
40
|
+
export function adapterForKind(kind) {
|
|
41
|
+
return Object.values(ADAPTERS).find((adapter) => adapter.kind === kind) ?? null;
|
|
42
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The React adapter: how to recognize a React package, mount a fixture, list its exports, template the simple archetypes,
|
|
3
|
+
* generate the rest, and tell esbuild to use one copy of React. Every framework adapter has this shape (see index.js).
|
|
4
|
+
*/
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { reactDialect } from "../harness/generate/dialects.js";
|
|
7
|
+
import { generateJsx } from "../harness/generate/jsx.js";
|
|
8
|
+
|
|
9
|
+
/** Mounts a fixture's default export. */
|
|
10
|
+
export const entry = (fixturePath, _pkg) => `import { createElement } from "react";
|
|
11
|
+
import { createRoot } from "react-dom/client";
|
|
12
|
+
import Fixture from ${JSON.stringify(fixturePath)};
|
|
13
|
+
const libA11y = new URLSearchParams(location.search).get("libA11y") === "on";
|
|
14
|
+
createRoot(document.getElementById("root")).render(createElement(Fixture, { libA11y }));
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
/** Loads the whole package, lists its exports, and records compound parts such as Dialog.Trigger. */
|
|
18
|
+
export const discoverEntry = (pkg) => `import * as lib from ${JSON.stringify(pkg)};
|
|
19
|
+
const out = [];
|
|
20
|
+
for (const name of Object.keys(lib)) {
|
|
21
|
+
const value = lib[name];
|
|
22
|
+
const type = typeof value;
|
|
23
|
+
if (value === null || (type !== "function" && type !== "object")) continue;
|
|
24
|
+
const parts = Object.keys(value).filter((key) => /^[A-Z]/.test(key)).slice(0, 30);
|
|
25
|
+
out.push({ name, type, parts });
|
|
26
|
+
}
|
|
27
|
+
window.__a11yExports = out;
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A fixture for the simple archetypes, from the export name alone.
|
|
32
|
+
* Compound components (dialog, tabs, menu) can't be guessed, so they come from generation or from a fixture someone writes.
|
|
33
|
+
*/
|
|
34
|
+
export function template(archetype, pkg, exportName) {
|
|
35
|
+
const body = {
|
|
36
|
+
button: `<Component data-a11y-trigger data-a11y-root type="button">Save</Component>`,
|
|
37
|
+
link: `<Component data-a11y-trigger data-a11y-root href="#top">Read more</Component>`,
|
|
38
|
+
}[archetype];
|
|
39
|
+
if (!body) return null;
|
|
40
|
+
return `import { ${exportName} as Component } from ${JSON.stringify(pkg)};
|
|
41
|
+
export default function Fixture() {
|
|
42
|
+
return ${body};
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default {
|
|
48
|
+
id: "react",
|
|
49
|
+
label: "React",
|
|
50
|
+
kind: "npm-react",
|
|
51
|
+
/** What the mapping calls a thing the package provides. */
|
|
52
|
+
noun: "export",
|
|
53
|
+
/** The extension of an authored fixture. */
|
|
54
|
+
extension: "jsx",
|
|
55
|
+
/** Packages the fixture and the library must share one copy of, and that the install makes sure are there. */
|
|
56
|
+
runtime: ["react", "react-dom"],
|
|
57
|
+
/** Is this package React, from its registry metadata alone? */
|
|
58
|
+
detect(meta) {
|
|
59
|
+
const peers = meta.peerDependencies ?? {};
|
|
60
|
+
const deps = meta.dependencies ?? {};
|
|
61
|
+
if (!("react" in peers || "react-dom" in peers || "react" in deps)) return null;
|
|
62
|
+
const how = "react" in peers || "react-dom" in peers ? "peer dependency" : "dependency";
|
|
63
|
+
return { kind: "npm-react", framework: "React", reason: `The package lists react as a ${how}.` };
|
|
64
|
+
},
|
|
65
|
+
/** esbuild settings for this framework. */
|
|
66
|
+
bundle: (workDir) => ({
|
|
67
|
+
// One copy of React for the library and the fixture, or hooks break.
|
|
68
|
+
alias: { react: join(workDir, "node_modules", "react"), "react-dom": join(workDir, "node_modules", "react-dom") },
|
|
69
|
+
esbuild: { jsx: "automatic" },
|
|
70
|
+
}),
|
|
71
|
+
entry,
|
|
72
|
+
discoverEntry,
|
|
73
|
+
template,
|
|
74
|
+
generate: (input) => generateJsx(reactDialect, input),
|
|
75
|
+
/** One phrase for the report: what the package was run as. */
|
|
76
|
+
describe: (npm) => `React${npm.react ? ` (react ${npm.react})` : ""}`,
|
|
77
|
+
};
|