ultra11y 5.22.0 → 5.23.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 +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.opencode/plugins/ultra11y.js +1 -1
- package/dist/playwright.mjs +4 -6
- package/package.json +1 -1
- package/scripts/build-pack-rgaa.mjs +36 -0
- package/scripts/ultra11y.mjs +7763 -7637
- package/scripts/vendor/wcag-2.2-fr.json +1 -1
- package/scripts/vendor/wcag-2.2-sc-universe.json +1 -1
- package/scripts/vendor/wcag-2.2-techniques.json +1 -1
- 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 +7763 -7637
- package/skills/ultra11y/SKILL.md +1 -1
- package/skills/ultra11y/scripts/ultra11y.mjs +7763 -7637
- package/src/adjudicate-subjects.ts +21 -0
- package/src/adjudicate.ts +2 -2
- package/src/audit.ts +14 -1
- package/src/coverage.ts +12 -3
- package/src/data/standards/rgaa.json +19 -1
- package/src/data/wcag-universe.json +1 -1
- package/src/probes.ts +13 -9
- package/src/scan.ts +6 -0
- package/src/snapshot.ts +13 -0
- package/src/standards/derive.ts +33 -7
- package/src/standards/pack-rules.ts +49 -1
- package/src/standards/registry.ts +16 -0
- package/src/standards/types.ts +27 -1
- package/src/standards/validate.ts +35 -1
- package/src/types.ts +1 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "ultra11y",
|
|
11
11
|
"source": "./",
|
|
12
12
|
"description": "Automatic accessibility review of the code under change before every commit, push and pull request, plus full WCAG 2.2 AA / RGAA audits and conformance deliverables on demand.",
|
|
13
|
-
"version": "5.
|
|
13
|
+
"version": "5.23.0",
|
|
14
14
|
"author": { "name": "maxgfr" },
|
|
15
15
|
"homepage": "https://github.com/maxgfr/ultra11y",
|
|
16
16
|
"repository": "https://github.com/maxgfr/ultra11y",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
3
|
"description": "Accessibility for WCAG 2.2 AA and country standards (RGAA…): an automatic review of the code under change before every commit, push and pull request, plus full audits and conformance deliverables on demand.",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.23.0",
|
|
5
5
|
"author": { "name": "maxgfr" },
|
|
6
6
|
"homepage": "https://github.com/maxgfr/ultra11y",
|
|
7
7
|
"repository": "https://github.com/maxgfr/ultra11y",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
3
|
"description": "Accessibility for WCAG 2.2 AA and country standards (RGAA…): an automatic review of the code under change before every commit, push and pull request, plus full audits and conformance deliverables on demand.",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.23.0",
|
|
5
5
|
"author": { "name": "maxgfr", "url": "https://github.com/maxgfr" },
|
|
6
6
|
"homepage": "https://github.com/maxgfr/ultra11y",
|
|
7
7
|
"repository": "https://github.com/maxgfr/ultra11y",
|
|
@@ -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.23.0";
|
|
31
31
|
|
|
32
32
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
33
33
|
|
package/dist/playwright.mjs
CHANGED
|
@@ -310,7 +310,7 @@ var FOCUS_WHERE_PROBE = `(() => { ${PRELUDE}
|
|
|
310
310
|
const e = document.activeElement;
|
|
311
311
|
if (!e || e === document.body || e === document.documentElement) return null;
|
|
312
312
|
const key = e.getAttribute && e.getAttribute('data-u11y-f');
|
|
313
|
-
return { key: key || __sel(e), selector: __sel(e), html: __html(e) };
|
|
313
|
+
return { key: key || __sel(e), tagged: !!key, selector: __sel(e), html: __html(e) };
|
|
314
314
|
})()`;
|
|
315
315
|
async function probeKeyboardTrap(page, limits = PROBE_DEFAULTS, deadline) {
|
|
316
316
|
const count = await page.evaluate(focusSetupExpr("", limits.maxFocusables));
|
|
@@ -325,13 +325,13 @@ async function probeKeyboardTrap(page, limits = PROBE_DEFAULTS, deadline) {
|
|
|
325
325
|
await page.keyboard.press("Tab");
|
|
326
326
|
const now = await page.evaluate(FOCUS_WHERE_PROBE);
|
|
327
327
|
if (!now) break;
|
|
328
|
-
if (prev && now.key === prev.key) {
|
|
328
|
+
if (prev?.tagged && now.tagged && now.key === prev.key) {
|
|
329
329
|
let stuck = true;
|
|
330
330
|
for (let k = 0; k < confirmPresses && stuck; k++) {
|
|
331
331
|
if (deadline?.out()) break;
|
|
332
332
|
await page.keyboard.press("Tab");
|
|
333
333
|
const again = await page.evaluate(FOCUS_WHERE_PROBE);
|
|
334
|
-
stuck = again !== null && again.key === now.key;
|
|
334
|
+
stuck = again !== null && again.tagged === true && again.key === now.key;
|
|
335
335
|
}
|
|
336
336
|
if (stuck) {
|
|
337
337
|
hits.push({
|
|
@@ -345,7 +345,6 @@ async function probeKeyboardTrap(page, limits = PROBE_DEFAULTS, deadline) {
|
|
|
345
345
|
if (seen.has(now.key)) break;
|
|
346
346
|
seen.add(now.key);
|
|
347
347
|
prev = now;
|
|
348
|
-
if (hits.length >= 4) break;
|
|
349
348
|
}
|
|
350
349
|
return hits;
|
|
351
350
|
}
|
|
@@ -432,8 +431,7 @@ async function runLiveProbes(page, opts = {}) {
|
|
|
432
431
|
}
|
|
433
432
|
};
|
|
434
433
|
if (!canResize) skip("1.4.10", "the page object cannot resize its viewport");
|
|
435
|
-
if (!canType)
|
|
436
|
-
if (!canType) skip("2.1.2", "the page object exposes no keyboard");
|
|
434
|
+
if (!canType) for (const sc of ["2.4.7", "2.1.2"]) skip(sc, "the page object exposes no keyboard");
|
|
437
435
|
if (!canHover) skip("1.4.13", "the page object cannot hover");
|
|
438
436
|
if (!canStyle) skip("1.4.12", "the page object cannot inject a stylesheet");
|
|
439
437
|
if (want("1.4.4")) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.23.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",
|
|
@@ -377,6 +377,42 @@ async function main() {
|
|
|
377
377
|
fr: 'Utilisez dir="rtl" ou dir="ltr" (ou dir="auto"), en accord avec le sens de lecture du texte porté.',
|
|
378
378
|
},
|
|
379
379
|
},
|
|
380
|
+
// RGAA 8.1 — « chaque page web est-elle définie par un type de document ? » THE criterion
|
|
381
|
+
// no engine could reach: it maps only onto WCAG 4.1.1, which WCAG 2.2 REMOVED, so
|
|
382
|
+
// `derivePackResults` classed it out of scope and left it « à évaluer » on every page of
|
|
383
|
+
// every run — the one criterion of the 106 that no measurement could ever close and only a
|
|
384
|
+
// model could. Measured on a real RGAA deliverable: a grid that sat at 105/106.
|
|
385
|
+
//
|
|
386
|
+
// Its subject is not an element, which is why no element rule could express it: the
|
|
387
|
+
// doctype is not part of `documentElement.outerHTML`. The collector records it beside the
|
|
388
|
+
// DOM (SnapshotMeta.doctype), and this DOCUMENT-level rule reads it there.
|
|
389
|
+
//
|
|
390
|
+
// WHAT IT DECIDES, AND WHAT IT DOES NOT. Test 8.1.1 (is a doctype present?) is the whole
|
|
391
|
+
// of it. 8.1.2 (is it valid?) and 8.1.3 (is it before <html>?) are not tested separately
|
|
392
|
+
// and deliberately so: a doctype the browser PARSED — which is the only kind that reaches
|
|
393
|
+
// `document.doctype` — is by construction well-formed and positioned ahead of the root
|
|
394
|
+
// element; one written after <html>, or malformed, is ignored by the parser and arrives
|
|
395
|
+
// here as the recorded empty string, i.e. as the absence this rule reports. Inventing a
|
|
396
|
+
// separate "invalid" verdict would mean guessing at legacy but legitimate declarations,
|
|
397
|
+
// and manufacturing a non-conformity is the one thing this tier must not do.
|
|
398
|
+
//
|
|
399
|
+
// NORMATIVE, and signal-gated: it fires only where a capture recorded the field, so a
|
|
400
|
+
// source file and a pre-field capture are both silence rather than a failure.
|
|
401
|
+
{
|
|
402
|
+
id: "doctype-missing",
|
|
403
|
+
criterion: "8.1",
|
|
404
|
+
wcag: ["4.1.1"],
|
|
405
|
+
severity: "majeur",
|
|
406
|
+
doc: { signal: "doctype", op: "absent" },
|
|
407
|
+
message: {
|
|
408
|
+
en: "The captured page declares no document type — the browser parsed no <!DOCTYPE> ahead of <html> (RGAA test 8.1.1).",
|
|
409
|
+
fr: "La page capturée ne déclare aucun type de document — le navigateur n’a analysé aucun <!DOCTYPE> avant <html> (test RGAA 8.1.1).",
|
|
410
|
+
},
|
|
411
|
+
remediation: {
|
|
412
|
+
en: "Emit <!DOCTYPE html> as the first line of the document, before <html>. A doctype placed after <html>, or malformed, is ignored by the parser and counts as absent.",
|
|
413
|
+
fr: "Émettez <!DOCTYPE html> en première ligne du document, avant <html>. Un doctype placé après <html>, ou malformé, est ignoré par l’analyseur et compte comme absent.",
|
|
414
|
+
},
|
|
415
|
+
},
|
|
380
416
|
];
|
|
381
417
|
// Wire each rule's namespaced id (`pack:rgaa:<id>`) into the criterion it reports under,
|
|
382
418
|
// so derivePackResults routes its finding onto that criterion through the same
|