ultra11y 5.22.1 → 5.24.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/README.md +2 -2
- package/package.json +1 -1
- package/scripts/build-pack-rgaa.mjs +63 -1
- package/scripts/ultra11y.mjs +7948 -7598
- 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 +7948 -7598
- package/skills/ultra11y/SKILL.md +3 -3
- package/skills/ultra11y/references/ci.md +1 -1
- package/skills/ultra11y/references/criteria.md +1 -1
- package/skills/ultra11y/references/judgment.md +1 -1
- package/skills/ultra11y/references/mcp.md +1 -1
- package/skills/ultra11y/scripts/ultra11y.mjs +7948 -7598
- package/src/adjudicate-subjects.ts +116 -2
- package/src/adjudicate.ts +11 -2
- package/src/audit.ts +14 -1
- package/src/coverage.ts +12 -3
- package/src/data/standards/rgaa.json +34 -2
- package/src/data/wcag-universe.json +1 -1
- package/src/data/wcag.json +4 -1
- package/src/llm.ts +1 -1
- package/src/messages.ts +42 -0
- package/src/rules/presentation.ts +165 -2
- package/src/scan.ts +6 -0
- package/src/snapshot.ts +13 -0
- package/src/standards/coverage.ts +1 -1
- package/src/standards/derive.ts +46 -8
- 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.24.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.24.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.24.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.24.0";
|
|
31
31
|
|
|
32
32
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
33
33
|
|
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ Full details, thresholds and off-switches under [Install](#install).</sub>
|
|
|
25
25
|
|
|
26
26
|
> Audit HTML/CSS/JSX against **WCAG 2.2 AA** accessibility and produce a dated compliance report — or author/review accessible markup without regressions. A [skills.sh](https://skills.sh) agent skill: a deterministic, zero-dependency static engine **plus** the agent's judgment, with `check`/`verify` gates against hallucinated non-conformities. **The central deliverable is the auditor conformance block** — theme, criterion + official wording, test(s), WCAG mapping + level, finding, expected state, verification, `file:line` occurrences — rendered identically by the `report` (compliance doc), the `prd` backlog and the GitHub issues, in the active standard's vocabulary and **in your language** (`--lang auto` follows the conversation/repo). **WCAG is the worldwide core; country standards (RGAA, …) are pluggable in-repo packs.**
|
|
27
27
|
|
|
28
|
-
ultra11y is built around an honest **division of labour**. Automated tools only catch a fraction of accessibility problems, so the engine does the *mechanical* work —
|
|
28
|
+
ultra11y is built around an honest **division of labour**. Automated tools only catch a fraction of accessibility problems, so the engine does the *mechanical* work — 81 machine-detectable static checks tied to the WCAG 2.2 success criteria — and is explicit about everything it can't decide statically. What it can't, the **AI agent adjudicates** (statically, from the evidence, gated by `verify`/`check`) — not a deferral to a human:
|
|
29
29
|
|
|
30
30
|
- **Automatable (engine):** missing `alt`/`lang`/`title`, unlabeled fields, empty links/buttons, icon-only controls, iframes without title, tables without headers, heading-level skips, empty/dangling headings & labels, duplicate ids, invalid/broken ARIA, positive `tabindex`, autoplay/timed-refresh/`blink`/`marquee` media…
|
|
31
31
|
- **Agent judgment (gated):** alt-text relevance, link purpose in context, reading/tab order, caption accuracy — the agent rules on these via `verify --manual` → `--apply`, each verdict carrying a justification (or a groundable NC), never a silent "conforming".
|
|
@@ -226,7 +226,7 @@ from auditing out of memory. `cwd` is optional on all five; it selects whose pac
|
|
|
226
226
|
`ultra11y_method` is the one to call first. It partitions the standard into evidence tiers —
|
|
227
227
|
`source`, `cross-file`, `rendered-page`, `browser`, `judgment`, `out-of-scope` — from the
|
|
228
228
|
engine's own per-criterion rule applicability, never from guessing at the wording of a test.
|
|
229
|
-
For RGAA it reports that **
|
|
229
|
+
For RGAA it reports that **56 of 106 criteria declare that no engine rule can evidence them**.
|
|
230
230
|
That is the standard saying they are yours, and a plan that hides it reads as coverage that
|
|
231
231
|
does not exist.
|
|
232
232
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.24.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",
|
|
@@ -310,7 +310,10 @@ async function main() {
|
|
|
310
310
|
// (link explicitness, WCAG 2.4.4). ADVISORY: it surfaces as a recommendation in the RGAA
|
|
311
311
|
// projection and NEVER makes 6.1 non-conformant. This proves a pack ships its OWN
|
|
312
312
|
// detection without forking the engine (see skills/ultra11y/references/packs.md).
|
|
313
|
-
|
|
313
|
+
// Kept in step with the engine instead of retyped: two hand-maintained copies of a rule-id
|
|
314
|
+
// list are two chances for the pack to claim an instrument the engine does not ship.
|
|
315
|
+
const PRESENTATIONAL_RULE_IDS = ["presentational-element", "presentational-attribute", "presentational-spacing"];
|
|
316
|
+
const DOWNLOAD_EXT = "pdf|docx?|pptx?|xlsx?|odt|ods|odp|rtf|csv|zip|rar|7z|gz|epub|mp3|mp4|avi|mov";
|
|
314
317
|
const rules = [
|
|
315
318
|
{
|
|
316
319
|
id: "download-link-format",
|
|
@@ -377,6 +380,42 @@ async function main() {
|
|
|
377
380
|
fr: 'Utilisez dir="rtl" ou dir="ltr" (ou dir="auto"), en accord avec le sens de lecture du texte porté.',
|
|
378
381
|
},
|
|
379
382
|
},
|
|
383
|
+
// RGAA 8.1 — « chaque page web est-elle définie par un type de document ? » THE criterion
|
|
384
|
+
// no engine could reach: it maps only onto WCAG 4.1.1, which WCAG 2.2 REMOVED, so
|
|
385
|
+
// `derivePackResults` classed it out of scope and left it « à évaluer » on every page of
|
|
386
|
+
// every run — the one criterion of the 106 that no measurement could ever close and only a
|
|
387
|
+
// model could. Measured on a real RGAA deliverable: a grid that sat at 105/106.
|
|
388
|
+
//
|
|
389
|
+
// Its subject is not an element, which is why no element rule could express it: the
|
|
390
|
+
// doctype is not part of `documentElement.outerHTML`. The collector records it beside the
|
|
391
|
+
// DOM (SnapshotMeta.doctype), and this DOCUMENT-level rule reads it there.
|
|
392
|
+
//
|
|
393
|
+
// WHAT IT DECIDES, AND WHAT IT DOES NOT. Test 8.1.1 (is a doctype present?) is the whole
|
|
394
|
+
// of it. 8.1.2 (is it valid?) and 8.1.3 (is it before <html>?) are not tested separately
|
|
395
|
+
// and deliberately so: a doctype the browser PARSED — which is the only kind that reaches
|
|
396
|
+
// `document.doctype` — is by construction well-formed and positioned ahead of the root
|
|
397
|
+
// element; one written after <html>, or malformed, is ignored by the parser and arrives
|
|
398
|
+
// here as the recorded empty string, i.e. as the absence this rule reports. Inventing a
|
|
399
|
+
// separate "invalid" verdict would mean guessing at legacy but legitimate declarations,
|
|
400
|
+
// and manufacturing a non-conformity is the one thing this tier must not do.
|
|
401
|
+
//
|
|
402
|
+
// NORMATIVE, and signal-gated: it fires only where a capture recorded the field, so a
|
|
403
|
+
// source file and a pre-field capture are both silence rather than a failure.
|
|
404
|
+
{
|
|
405
|
+
id: "doctype-missing",
|
|
406
|
+
criterion: "8.1",
|
|
407
|
+
wcag: ["4.1.1"],
|
|
408
|
+
severity: "majeur",
|
|
409
|
+
doc: { signal: "doctype", op: "absent" },
|
|
410
|
+
message: {
|
|
411
|
+
en: "The captured page declares no document type — the browser parsed no <!DOCTYPE> ahead of <html> (RGAA test 8.1.1).",
|
|
412
|
+
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).",
|
|
413
|
+
},
|
|
414
|
+
remediation: {
|
|
415
|
+
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.",
|
|
416
|
+
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.",
|
|
417
|
+
},
|
|
418
|
+
},
|
|
380
419
|
];
|
|
381
420
|
// Wire each rule's namespaced id (`pack:rgaa:<id>`) into the criterion it reports under,
|
|
382
421
|
// so derivePackResults routes its finding onto that criterion through the same
|
|
@@ -387,6 +426,28 @@ async function main() {
|
|
|
387
426
|
c.appliesTo = { ruleIds: [...new Set([...c.appliesTo.ruleIds, `pack:rgaa:${rule.id}`])].sort() };
|
|
388
427
|
}
|
|
389
428
|
|
|
429
|
+
// ---- RGAA 10.1 — the engine's presentational-markup rules, and their normativity ----
|
|
430
|
+
//
|
|
431
|
+
// 10.1's three tests are « these must be absent », over lists the RGAA closes itself: the
|
|
432
|
+
// glossary entry « Présentation de l'information » names the forbidden elements and
|
|
433
|
+
// attributes. The engine ships the instruments (src/rules/presentation.ts); the pack says
|
|
434
|
+
// they decide THIS criterion, and re-normativizes them.
|
|
435
|
+
//
|
|
436
|
+
// They ship ADVISORY because `<center>` is obsolete HTML rather than a WCAG failure —
|
|
437
|
+
// nothing about it breaks assistive technology, and reporting a WCAG 1.3.1 non-conformity
|
|
438
|
+
// for it would be wrong. Under the RGAA it is a plain failure of test 10.1.1, so the pack
|
|
439
|
+
// flips it here. This is what `overrides` is for, and it is the first use of it.
|
|
440
|
+
//
|
|
441
|
+
// Before this, 10.1 had NO instrument and inherited `readingOrder` through WCAG 1.3.2 — a
|
|
442
|
+
// subject that answers a different question — so it reached the adjudicator with an empty
|
|
443
|
+
// harvest. Measured on run 32508717451 (Sonnet, RGAA, 3 passes): the model ruled `C` three
|
|
444
|
+
// times, the gate refused all three for citing nothing, and 10.1 was the ONE criterion of
|
|
445
|
+
// the 106 left « à évaluer », on the run and on every page.
|
|
446
|
+
const presentational = criteria.find((x) => x.id === "10.1");
|
|
447
|
+
if (!presentational) throw new Error("build-pack-rgaa: RGAA 10.1 is missing from the referential");
|
|
448
|
+
presentational.appliesTo = { ruleIds: [...new Set([...presentational.appliesTo.ruleIds, ...PRESENTATIONAL_RULE_IDS])].sort() };
|
|
449
|
+
const overrides = Object.fromEntries(PRESENTATIONAL_RULE_IDS.map((id) => [id, { advisory: false, severity: "majeur" }]));
|
|
450
|
+
|
|
390
451
|
const pack = {
|
|
391
452
|
key: "rgaa",
|
|
392
453
|
name: "RGAA",
|
|
@@ -469,6 +530,7 @@ async function main() {
|
|
|
469
530
|
},
|
|
470
531
|
],
|
|
471
532
|
rules,
|
|
533
|
+
overrides,
|
|
472
534
|
themes,
|
|
473
535
|
criteria,
|
|
474
536
|
};
|