ultra11y 5.42.0 → 5.43.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.
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +3 -3
- package/.codex-plugin/plugin.json +4 -4
- package/.opencode/plugins/ultra11y.js +1 -1
- package/README.md +10 -10
- package/dist/cypress-plugin.d.ts +1 -1
- package/dist/cypress.d.ts +2 -2
- package/dist/{payload-Bb1uQH1q.d.ts → payload-BMFQTm5s.d.ts} +4 -1
- package/dist/playwright.d.ts +1 -1
- package/dist/playwright.mjs +30 -3
- package/package.json +3 -3
- package/scripts/build-pack-rgaa.mjs +9 -0
- package/scripts/ultra11y.mjs +3992 -2901
- package/scripts/vendor/wcag-2.2-fr.json +95 -78
- package/scripts/vendor/wcag-2.2-sc-universe.json +1 -1
- package/scripts/vendor/wcag-2.2-techniques.json +5 -5
- package/scripts/vendor/wcag-2.2-text.json +1 -1
- package/skills/review-a11y/SKILL.md +1 -1
- package/skills/review-a11y/scripts/ultra11y.mjs +3992 -2901
- package/skills/ultra11y/SKILL.md +1 -1
- package/skills/ultra11y/references/audit.md +7 -5
- package/skills/ultra11y/references/ci.md +12 -12
- package/skills/ultra11y/references/dynamic.md +1 -1
- package/skills/ultra11y/references/harnesses.md +3 -3
- package/skills/ultra11y/references/methodology.md +44 -6
- package/skills/ultra11y/references/verify.md +2 -0
- package/skills/ultra11y/scripts/ultra11y.mjs +3992 -2901
- package/skills/ultra11y/templates/gitlab-ci.yml +2 -2
- package/src/audit.ts +8 -1
- package/src/auditor.ts +128 -4
- package/src/check.ts +34 -2
- package/src/cli.ts +21 -11
- package/src/data/guidance/wcag.json +2 -2
- package/src/data/standards/rgaa.json +23 -0
- package/src/data/wcag-universe.json +1 -1
- package/src/data/wcag.json +67 -69
- package/src/graph/engine-resolve.ts +0 -0
- package/src/html-report.ts +91 -7
- package/src/init.ts +3 -3
- package/src/install/codex.ts +2 -2
- package/src/install/index.ts +1 -1
- package/src/integrations/payload.ts +4 -1
- package/src/integrations/playwright.ts +14 -2
- package/src/mcp/handlers.ts +7 -2
- package/src/md.ts +15 -1
- package/src/pages.ts +65 -0
- package/src/probes.ts +57 -1
- package/src/report.ts +1045 -269
- package/src/sarif.ts +1 -1
- package/src/scan.ts +10 -0
- package/src/snapshot.ts +7 -0
- package/src/standards/types.ts +12 -0
- package/src/standards/validate.ts +21 -0
- package/src/types.ts +3 -1
- package/src/util.ts +7 -0
- package/src/vendor/codeindex-engine.d.mts +4 -2
- package/src/vendor/codeindex-engine.mjs +565 -358
- package/scripts/check-artifact-recall.mjs +0 -199
- package/scripts/sync-engine.mjs +0 -158
- package/src/vendor/engine.meta.json +0 -9
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
"name": "ultra11y",
|
|
11
11
|
"source": "./",
|
|
12
12
|
"description": "Automatic accessibility review before commit, push and pull request, plus detailed evidenced Markdown, HTML and per-page WCAG 2.2 AA / RGAA reports in Claude Code.",
|
|
13
|
-
"version": "5.
|
|
13
|
+
"version": "5.43.0",
|
|
14
14
|
"author": { "name": "maxgfr" },
|
|
15
|
-
"homepage": "https://github.com/
|
|
16
|
-
"repository": "https://github.com/
|
|
15
|
+
"homepage": "https://github.com/SocialGouv/ultra11y",
|
|
16
|
+
"repository": "https://github.com/SocialGouv/ultra11y",
|
|
17
17
|
"license": "MIT",
|
|
18
18
|
"category": "quality",
|
|
19
19
|
"keywords": ["accessibility", "a11y", "wcag", "rgaa", "code-review", "audit"]
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
3
|
"description": "Accessibility for WCAG 2.2 AA and country standards (RGAA…): automatic review before commit, push and pull request, plus detailed evidenced Markdown, HTML and per-page conformance reports in interactive agent sessions.",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.43.0",
|
|
5
5
|
"author": { "name": "maxgfr" },
|
|
6
|
-
"homepage": "https://github.com/
|
|
7
|
-
"repository": "https://github.com/
|
|
6
|
+
"homepage": "https://github.com/SocialGouv/ultra11y",
|
|
7
|
+
"repository": "https://github.com/SocialGouv/ultra11y",
|
|
8
8
|
"license": "MIT"
|
|
9
9
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
3
|
"description": "Accessibility for WCAG 2.2 AA and country standards (RGAA…): automatic review before commit, push and pull request, plus detailed evidenced Markdown, HTML and per-page conformance reports in interactive agent sessions.",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.43.0",
|
|
5
5
|
"author": { "name": "maxgfr", "url": "https://github.com/maxgfr" },
|
|
6
|
-
"homepage": "https://github.com/
|
|
7
|
-
"repository": "https://github.com/
|
|
6
|
+
"homepage": "https://github.com/SocialGouv/ultra11y",
|
|
7
|
+
"repository": "https://github.com/SocialGouv/ultra11y",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"keywords": ["accessibility", "a11y", "wcag", "rgaa", "code-review", "audit"],
|
|
10
10
|
"skills": "./skills/",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"developerName": "maxgfr",
|
|
16
16
|
"category": "Productivity",
|
|
17
17
|
"capabilities": ["Read", "Write"],
|
|
18
|
-
"websiteURL": "https://github.com/
|
|
18
|
+
"websiteURL": "https://github.com/SocialGouv/ultra11y",
|
|
19
19
|
"defaultPrompt": "Review my staged changes for accessibility."
|
|
20
20
|
}
|
|
21
21
|
}
|
|
@@ -27,7 +27,7 @@ import { fileURLToPath } from "node:url";
|
|
|
27
27
|
// Ownership marker: `ultra11y install --opencode` refuses to overwrite a plugin file that
|
|
28
28
|
// does not carry it, so a user's own ultra11y.js is never clobbered.
|
|
29
29
|
const ULTRA11Y_OPENCODE_PLUGIN = "ULTRA11Y_OPENCODE_PLUGIN";
|
|
30
|
-
const ULTRA11Y_PLUGIN_VERSION = "5.
|
|
30
|
+
const ULTRA11Y_PLUGIN_VERSION = "5.43.0";
|
|
31
31
|
|
|
32
32
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
33
33
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ultra11y
|
|
2
2
|
|
|
3
|
-
[](https://github.com/SocialGouv/ultra11y/actions/workflows/ci.yml)
|
|
4
4
|
[](https://www.npmjs.com/package/ultra11y)
|
|
5
5
|
[](#add-to-your-coding-agent)
|
|
6
6
|
[](https://nodejs.org)
|
|
@@ -13,12 +13,12 @@ pull request — you never type the skill's name:
|
|
|
13
13
|
|
|
14
14
|
| | |
|
|
15
15
|
|---|---|
|
|
16
|
-
| **Claude Code** | `/plugin marketplace add
|
|
17
|
-
| **OpenAI Codex CLI** | `codex plugin marketplace add
|
|
16
|
+
| **Claude Code** | `/plugin marketplace add SocialGouv/ultra11y` then `/plugin install ultra11y@ultra11y` |
|
|
17
|
+
| **OpenAI Codex CLI** | `codex plugin marketplace add SocialGouv/ultra11y` then `codex plugin add ultra11y@ultra11y` |
|
|
18
18
|
| **OpenCode** | `npx ultra11y install --opencode` — or pin `"plugin": ["ultra11y@latest"]` in `opencode.json` |
|
|
19
19
|
| **Anything else** | `npx ultra11y install --agents-md` (no hook API: see the caveat below) |
|
|
20
20
|
|
|
21
|
-
<sub>Prefer the skills only, without the automatic hook? `npx skills add
|
|
21
|
+
<sub>Prefer the skills only, without the automatic hook? `npx skills add SocialGouv/ultra11y`
|
|
22
22
|
(`--agent codex`, `--agent opencode`, …). Already on npm and just want the gate?
|
|
23
23
|
`npx ultra11y install --all`, and `npx ultra11y status` to check what is actually wired.
|
|
24
24
|
Full details, thresholds and off-switches under [Install](#install).</sub>
|
|
@@ -73,8 +73,8 @@ working diff, or branch vs merge-base) and reports like a code reviewer. Both bu
|
|
|
73
73
|
install-free engine.
|
|
74
74
|
|
|
75
75
|
```sh
|
|
76
|
-
npx skills add
|
|
77
|
-
npx skills add
|
|
76
|
+
npx skills add SocialGouv/ultra11y # this agent
|
|
77
|
+
npx skills add SocialGouv/ultra11y --agent codex # …or opencode, cursor, gemini, amp, zed…
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
**As an agent plugin** — the same two skills, plus the hook that makes the review
|
|
@@ -83,8 +83,8 @@ that carries accessibility findings is stopped once and the agent is handed the
|
|
|
83
83
|
so `review-a11y` runs before the work goes out — you never type the skill's name.
|
|
84
84
|
|
|
85
85
|
```
|
|
86
|
-
/plugin marketplace add
|
|
87
|
-
codex plugin marketplace add
|
|
86
|
+
/plugin marketplace add SocialGouv/ultra11y # Claude Code
|
|
87
|
+
codex plugin marketplace add SocialGouv/ultra11y # Codex CLI
|
|
88
88
|
npx ultra11y install --opencode # OpenCode
|
|
89
89
|
```
|
|
90
90
|
|
|
@@ -125,7 +125,7 @@ claude mcp add ultra11y -- npx -y ultra11y mcp
|
|
|
125
125
|
**As a GitHub Action** — audits the PR diff and, optionally, the served pages:
|
|
126
126
|
|
|
127
127
|
```yaml
|
|
128
|
-
- uses:
|
|
128
|
+
- uses: SocialGouv/ultra11y@v5
|
|
129
129
|
with: { since: auto, standard: rgaa, fail-on: blocking }
|
|
130
130
|
```
|
|
131
131
|
|
|
@@ -436,7 +436,7 @@ is the source of truth; CI rebuilds it from the vendored DINUM data and rejects
|
|
|
436
436
|
block. It re-decides nothing and invents no format, so a page sheet and the compliance report
|
|
437
437
|
cannot disagree; `check` gates it against invented criteria like any other report.
|
|
438
438
|
See `references/pages.md`.
|
|
439
|
-
- **A shipped GitHub Action** — `uses:
|
|
439
|
+
- **A shipped GitHub Action** — `uses: SocialGouv/ultra11y@v5` audits the **code** (PR diff) and,
|
|
440
440
|
optionally, the **pages** (it can start your app, wait for it, then scan real URLs or your
|
|
441
441
|
declared sample). The engine ships inside the action, so there is nothing to install and no
|
|
442
442
|
`setup-node`. With pages in scope, its job summary and `pages-status.md` artifact list every
|
package/dist/cypress-plugin.d.ts
CHANGED
package/dist/cypress.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as CheckOptions } from './payload-
|
|
2
|
-
export { s as slugify } from './payload-
|
|
1
|
+
import { C as CheckOptions } from './payload-BMFQTm5s.js';
|
|
2
|
+
export { s as slugify } from './payload-BMFQTm5s.js';
|
|
3
3
|
|
|
4
4
|
interface CypressCheckOptions extends CheckOptions {
|
|
5
5
|
/** Capture a viewport screenshot so the pixel tier can run. On by default — the plugin
|
|
@@ -80,7 +80,10 @@ interface CheckOptions {
|
|
|
80
80
|
* for nothing but recording the sample and asserts nothing afterwards. `{ clicks: true }`
|
|
81
81
|
* additionally presses `button[type=button]` (destructive-sounding names skipped, navigation
|
|
82
82
|
* aborts the pass) — never the default on an authenticated session, where a server mutation
|
|
83
|
-
* is invisible to any check this side of the network.
|
|
83
|
+
* is invisible to any check this side of the network.
|
|
84
|
+
*
|
|
85
|
+
* PLAYWRIGHT ONLY, like `probes` beside it: the Cypress integration records a page and runs
|
|
86
|
+
* no live probe at all, so both are inert there. Stated rather than left to be discovered. */
|
|
84
87
|
liveRegion?: boolean | {
|
|
85
88
|
clicks?: boolean;
|
|
86
89
|
};
|
package/dist/playwright.d.ts
CHANGED
package/dist/playwright.mjs
CHANGED
|
@@ -133,6 +133,7 @@ var COLLECT_SNAPSHOT = `(() => {
|
|
|
133
133
|
})()`;
|
|
134
134
|
|
|
135
135
|
// src/probes.ts
|
|
136
|
+
var PROBES_VERSION = 2;
|
|
136
137
|
var PRELUDE = `
|
|
137
138
|
const __sel = (e) => {
|
|
138
139
|
if (!e || !e.tagName) return '\u2014';
|
|
@@ -240,8 +241,18 @@ function focusSetupExpr(scope = "", maxFocusables = PROBE_DEFAULTS.maxFocusables
|
|
|
240
241
|
// from it is not merely unmeasured -- it is silently cleared. A page whose only control is a
|
|
241
242
|
// <summary> tagged nothing at all, so the count was zero, so the ring was "vacuously whole",
|
|
242
243
|
// so 2.4.7 and 2.4.11 closed without a single Tab press.
|
|
244
|
+
//
|
|
245
|
+
// AND NOT iframe / audio[controls] / video[controls], which were here for one release and
|
|
246
|
+
// had to come back out. They are genuinely focusable, but their focus lives in another
|
|
247
|
+
// document: everything below reads the PARENT'S activeElement, which stays the host element
|
|
248
|
+
// press after press while the user tabs through the controls inside. The trap walk reads
|
|
249
|
+
// exactly that as a cage, so an ordinary page with a video player, a payment frame, a map or
|
|
250
|
+
// a support widget was reported as a bloquant 2.1.2 -- a blocker manufactured out of our own
|
|
251
|
+
// blindness, able to fail somebody else's gate. The measurement we cannot make is not a
|
|
252
|
+
// finding; Tab still crosses them, the walk still records that it crossed something it never
|
|
253
|
+
// measured, and the page is not cleared either.
|
|
243
254
|
// (No backticks in this comment: it lives inside a template literal.)
|
|
244
|
-
const sel = 'a[href],area[href],button:not([disabled]),input:not([type=hidden]):not([disabled]),select:not([disabled]),textarea:not([disabled]),summary,
|
|
255
|
+
const sel = 'a[href],area[href],button:not([disabled]),input:not([type=hidden]):not([disabled]),select:not([disabled]),textarea:not([disabled]),summary,[contenteditable]:not([contenteditable="false"]),[tabindex]:not([tabindex="-1"]),[role=button]:not([disabled])';
|
|
245
256
|
// A BOX THAT IS ANIMATING CANNOT BE COMPARED ACROSS TIME, so it contributes a constant.
|
|
246
257
|
//
|
|
247
258
|
// The snapshot is taken before Tab and re-read after it, and the properties compared include
|
|
@@ -587,6 +598,7 @@ async function runLiveProbes(page, opts = {}) {
|
|
|
587
598
|
const size = canResize ? page.viewportSize() ?? null : null;
|
|
588
599
|
const restore = size ?? { width: 1280, height: 900 };
|
|
589
600
|
const out = {
|
|
601
|
+
v: PROBES_VERSION,
|
|
590
602
|
focusVisible: [],
|
|
591
603
|
hover: [],
|
|
592
604
|
keyboardTrap: [],
|
|
@@ -703,6 +715,12 @@ async function runLiveProbes(page, opts = {}) {
|
|
|
703
715
|
else skip("4.1.3", r.why ?? "the live-region pass did not exercise the whole page");
|
|
704
716
|
}
|
|
705
717
|
}
|
|
718
|
+
for (const [bucket, sc] of UNREADABLE_BY_OLDER) {
|
|
719
|
+
if (out[bucket]?.length) {
|
|
720
|
+
const at = out.probed.indexOf(sc);
|
|
721
|
+
if (at >= 0) out.probed.splice(at, 1);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
706
724
|
return out;
|
|
707
725
|
}
|
|
708
726
|
var DESTRUCTIVE_NAME_RE = "\\b(supprim|retir|effac|envoy|valid|confirm|pay|achet|command|delete|remove|eras|clear|send|submit|buy|order)";
|
|
@@ -856,6 +874,11 @@ var REMOVE_TEXT_SPACING_STEP = `(() => {
|
|
|
856
874
|
}
|
|
857
875
|
return true;
|
|
858
876
|
})()`;
|
|
877
|
+
var UNREADABLE_BY_OLDER = [
|
|
878
|
+
["focusObscured", "2.4.11"],
|
|
879
|
+
["keyboardTrap", "2.1.2"],
|
|
880
|
+
["liveRegion", "4.1.3"]
|
|
881
|
+
];
|
|
859
882
|
|
|
860
883
|
// src/integrations/core.ts
|
|
861
884
|
import { spawnSync } from "child_process";
|
|
@@ -1055,9 +1078,13 @@ async function checkA11y(page, opts = {}) {
|
|
|
1055
1078
|
);
|
|
1056
1079
|
}
|
|
1057
1080
|
let probes;
|
|
1058
|
-
if (opts.probes) {
|
|
1081
|
+
if (opts.probes || opts.liveRegion) {
|
|
1059
1082
|
try {
|
|
1060
|
-
probes = await runLiveProbes(page, {
|
|
1083
|
+
probes = await runLiveProbes(page, {
|
|
1084
|
+
...probeOptions(opts.probes),
|
|
1085
|
+
...opts.probes ? {} : { only: ["4.1.3"] },
|
|
1086
|
+
...opts.liveRegion ? { liveRegion: opts.liveRegion } : {}
|
|
1087
|
+
});
|
|
1061
1088
|
} catch (e) {
|
|
1062
1089
|
console.warn(
|
|
1063
1090
|
`ultra11y: the live probes failed on this page \u2014 ${e instanceof Error ? e.message : String(e)}. The snapshot was still recorded; the criteria they decide stay to assess.`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.43.0",
|
|
4
4
|
"description": "Audit HTML/CSS/JSX against WCAG 2.2 AA and produce a dated compliance report, or author/review accessible markup (native-HTML-first). A skills.sh agent skill: an install-free static engine (bundled, no npm install) plus the agent's judgment, with check/verify gates against hallucinated non-conformities, and pluggable in-repo standards packs (RGAA France first; Section 508 & EN 301 549 next).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@10.33.0",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"lint:fix": "biome check --write .",
|
|
56
56
|
"format": "biome format --write .",
|
|
57
57
|
"verify:bundle": "node scripts/verify-skill-bundle.mjs",
|
|
58
|
-
"check:build": "tsup && node scripts/copy-bundle.mjs && git diff --exit-code -- scripts/ultra11y.mjs skills/ultra11y/scripts/ultra11y.mjs skills/review-a11y/scripts/ultra11y.mjs dist && node scripts/build-extension-icons.mjs --check && node scripts/verify-skill-bundle.mjs
|
|
58
|
+
"check:build": "tsup && node scripts/copy-bundle.mjs && git diff --exit-code -- scripts/ultra11y.mjs skills/ultra11y/scripts/ultra11y.mjs skills/review-a11y/scripts/ultra11y.mjs dist && node scripts/build-extension-icons.mjs --check && node scripts/verify-skill-bundle.mjs",
|
|
59
59
|
"build:wcag": "node scripts/build-standards.mjs",
|
|
60
60
|
"check:standards": "node scripts/build-standards.mjs --check",
|
|
61
61
|
"build:pack:rgaa": "node scripts/build-pack-rgaa.mjs",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"author": "maxgfr",
|
|
76
76
|
"repository": {
|
|
77
77
|
"type": "git",
|
|
78
|
-
"url": "https://github.com/
|
|
78
|
+
"url": "https://github.com/SocialGouv/ultra11y"
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
81
|
"@axe-core/playwright": "^4.13.0",
|
|
@@ -711,6 +711,15 @@ const DOWNLOAD_EXT = "pdf|docx?|pptx?|xlsx?|odt|ods|odp|rtf|csv|zip|rar|7z|gz|ep
|
|
|
711
711
|
// Where DINUM publishes each criterion. Cited by the adjudication brief so a reader can
|
|
712
712
|
// reach the normative page — the vendored text above stays the authority either way.
|
|
713
713
|
criterionUrl: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#{id}",
|
|
714
|
+
// The three levels a French declaration of accessibility may state, fixed by the arrêté of
|
|
715
|
+
// 20 September 2019 (accessibilite.numerique.gouv.fr, obligations): every applicable
|
|
716
|
+
// criterion validated, at least half of them, or fewer. The report summary names the
|
|
717
|
+
// audited site's level from this scale and says what separates it from the next one.
|
|
718
|
+
conformityLevels: [
|
|
719
|
+
{ min: 100, label: { fr: "Totalement conforme", en: "Fully compliant" } },
|
|
720
|
+
{ min: 50, label: { fr: "Partiellement conforme", en: "Partially compliant" } },
|
|
721
|
+
{ min: 0, label: { fr: "Non conforme", en: "Non-compliant" } },
|
|
722
|
+
],
|
|
714
723
|
// Auditor-display vocabulary (FR): the nouns an RGAA auditor reads. Rendered by the
|
|
715
724
|
// `prd` auditor block + GitHub issues; see src/standards/vocabulary.ts.
|
|
716
725
|
vocabulary: {
|