ultra11y 5.3.1 → 5.3.3
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/package.json +1 -1
- package/scripts/ultra11y.mjs +21 -9
- package/skills/review-a11y/SKILL.md +1 -1
- package/skills/review-a11y/scripts/ultra11y.mjs +21 -9
- package/skills/ultra11y/SKILL.md +1 -1
- package/skills/ultra11y/scripts/ultra11y.mjs +21 -9
- package/src/annotate.ts +36 -9
- 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.3.
|
|
13
|
+
"version": "5.3.3",
|
|
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.3.
|
|
4
|
+
"version": "5.3.3",
|
|
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.3.
|
|
4
|
+
"version": "5.3.3",
|
|
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.3.
|
|
30
|
+
const ULTRA11Y_PLUGIN_VERSION = "5.3.3";
|
|
31
31
|
|
|
32
32
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
33
33
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.3",
|
|
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",
|
package/scripts/ultra11y.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { join as join50, relative as relative5, resolve as resolve15, sep as sep
|
|
|
6
6
|
import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
|
|
7
7
|
|
|
8
8
|
// src/types.ts
|
|
9
|
-
var VERSION = "5.3.
|
|
9
|
+
var VERSION = "5.3.3";
|
|
10
10
|
var SCHEMA_VERSION = 2;
|
|
11
11
|
|
|
12
12
|
// src/audit.ts
|
|
@@ -60058,10 +60058,14 @@ var S = {
|
|
|
60058
60058
|
clamped: (n) => `_${n} groupe(s) retir\xE9(s) de ce commentaire pour tenir dans la limite de GitHub \u2014 le r\xE9sum\xE9 de job les porte tous._`,
|
|
60059
60059
|
unanchored: (n) => `${n} constat(s) rattach\xE9(s) \xE0 une URL, sans ligne de code \xE0 annoter \u2014 voir le rapport.`,
|
|
60060
60060
|
unattributed: (n) => `${n} constat(s) ne sont rattach\xE9s \xE0 aucune page (code partag\xE9, fichier hors routes) \u2014 compt\xE9s dans l'audit global, jamais r\xE9partis d'office.`,
|
|
60061
|
-
sourceBasis: "Une page marqu\xE9e \xAB source \xBB n'a pas d'instantan\xE9 : l'absence de constat n'y vaut PAS conformit\xE9
|
|
60061
|
+
sourceBasis: "Une page marqu\xE9e \xAB source \xBB n'a pas d'instantan\xE9 : l'absence de constat n'y vaut PAS conformit\xE9. Ses crit\xE8res non d\xE9cid\xE9s restent \xAB \xE0 \xE9valuer \xBB, ils ne basculent jamais en conformes par silence.",
|
|
60062
60062
|
pageByPage: "page par page",
|
|
60063
60063
|
pagesCount: (n) => `${n} page(s)`,
|
|
60064
60064
|
testsCol: "Tests",
|
|
60065
|
+
conformingCol: "C",
|
|
60066
|
+
nonConformingCol: "NC",
|
|
60067
|
+
toAssessCol: "\xC0 \xE9valuer",
|
|
60068
|
+
scoreboardNote: "`C` conforme \xB7 `NC` non conforme \xB7 \xAB \xC0 \xE9valuer \xBB : personne ne l'a encore tranch\xE9 \u2014 ni conforme ni non conforme. Pas de pourcentage ici : un taux calcul\xE9 sur les seuls crit\xE8res d\xE9cid\xE9s se lit comme une note de page, et vaut la m\xEAme chose sur une bonne page que sur une mauvaise. Le taux et sa couverture sont dans la fiche par page de l'artefact.",
|
|
60065
60069
|
noPages: "Aucune page dans le p\xE9rim\xE8tre de ce run : le balayage n'a produit aucun instantan\xE9. Ce n'est pas un bilan vide, c'est un bilan absent \u2014 les crit\xE8res au rendu restent \xE0 \xE9valuer.",
|
|
60066
60070
|
pagesDetailNote: "Un bloc par page portant au moins une non-conformit\xE9, et seulement ses crit\xE8res **non conformes** \u2014 la grille compl\xE8te (tous les crit\xE8res de chaque page, avec leurs tests et leurs captures) vit dans l'artefact.",
|
|
60067
60071
|
pagesClamped: (n) => `_Le d\xE9tail de ${n} page(s) a \xE9t\xE9 retir\xE9 de ce commentaire pour tenir dans la limite de GitHub \u2014 l'artefact les porte toutes._`,
|
|
@@ -60099,10 +60103,14 @@ var S = {
|
|
|
60099
60103
|
clamped: (n) => `_${n} group(s) dropped from this comment to fit GitHub's limit \u2014 the job summary carries them all._`,
|
|
60100
60104
|
unanchored: (n) => `${n} finding(s) keyed to a URL, with no code line to annotate \u2014 see the report.`,
|
|
60101
60105
|
unattributed: (n) => `${n} finding(s) are attributed to no page (shared code, file outside any route) \u2014 counted in the overall audit, never spread across pages.`,
|
|
60102
|
-
sourceBasis: 'A page marked "source" has no snapshot: the absence of a finding there does NOT mean conforming
|
|
60106
|
+
sourceBasis: 'A page marked "source" has no snapshot: the absence of a finding there does NOT mean conforming. Its undecided criteria stay \u201Cto assess\u201D; they never turn conforming by silence.',
|
|
60103
60107
|
pageByPage: "page by page",
|
|
60104
60108
|
pagesCount: (n) => `${n} page(s)`,
|
|
60105
60109
|
testsCol: "Tests",
|
|
60110
|
+
conformingCol: "C",
|
|
60111
|
+
nonConformingCol: "NC",
|
|
60112
|
+
toAssessCol: "To assess",
|
|
60113
|
+
scoreboardNote: "`C` conforming \xB7 `NC` non-conforming \xB7 \u201CTo assess\u201D: nobody has ruled on it yet \u2014 neither conforming nor not. No percentage here: a rate over the decided criteria alone reads as a page score, and reads the same on a good page as on a bad one. The rate and its coverage live in the artifact's per-page sheet.",
|
|
60106
60114
|
noPages: "No page in this run's scope: the sweep produced no snapshot. This is not an empty scoreboard, it is a missing one \u2014 the rendering criteria stay to assess.",
|
|
60107
60115
|
pagesDetailNote: "One block per page carrying at least one non-conformity, and only its **non-conforming** criteria \u2014 the full grid (every criterion of every page, with its tests and its screenshot) lives in the artifact.",
|
|
60108
60116
|
pagesClamped: (n) => `_The detail of ${n} page(s) was dropped from this comment to fit GitHub's limit \u2014 the artifact carries them all._`,
|
|
@@ -60223,14 +60231,18 @@ function perPageTable(result, standard = CORE2, lang = "en") {
|
|
|
60223
60231
|
if (!scope.length) return "";
|
|
60224
60232
|
attributePages(result, scope);
|
|
60225
60233
|
const derived = derivePages(result, scope);
|
|
60226
|
-
return [`### ${s.perPage}`, "", ...scoreboardTable(derived, s, lang), "", ...basisCaveats(result, derived, s, lang)].join("\n");
|
|
60234
|
+
return [`### ${s.perPage}`, "", ...scoreboardTable(result, derived, standard, s, lang), "", ...basisCaveats(result, derived, s, lang)].join("\n");
|
|
60227
60235
|
}
|
|
60228
|
-
function scoreboardTable(derived, s, lang) {
|
|
60229
|
-
const out2 = [
|
|
60236
|
+
function scoreboardTable(result, derived, standard, s, lang) {
|
|
60237
|
+
const out2 = [
|
|
60238
|
+
`| ${s.page} | ${s.basis} | ${s.conformingCol} | ${s.nonConformingCol} | ${s.toAssessCol} | \u{1F534} | \u{1F7E0} | \u{1F7E1} |`,
|
|
60239
|
+
"| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: |"
|
|
60240
|
+
];
|
|
60230
60241
|
for (const pg of derived) {
|
|
60231
60242
|
const n = (sev) => severityCount(pg, sev);
|
|
60243
|
+
const t2 = pageTally(pageCriterionRows(result, pg, standard, lang));
|
|
60232
60244
|
out2.push(
|
|
60233
|
-
`| ${pg.name}${pg.auth ? " \u{1F512}" : ""} \u2014 \`${pg.url}\` | ${basisLabel(pg.basis, lang)} | ${
|
|
60245
|
+
`| ${pg.name}${pg.auth ? " \u{1F512}" : ""} \u2014 \`${pg.url}\` | ${basisLabel(pg.basis, lang)} | ${t2.c} | ${t2.nc} | ${t2.manual} | ${n("bloquant")} | ${n("majeur")} | ${n("mineur")} |`
|
|
60234
60246
|
);
|
|
60235
60247
|
}
|
|
60236
60248
|
return out2;
|
|
@@ -60239,7 +60251,7 @@ function severityCount(pg, sev) {
|
|
|
60239
60251
|
return pg.findings.filter((f) => !f.advisory && f.severity === sev).length;
|
|
60240
60252
|
}
|
|
60241
60253
|
function basisCaveats(result, derived, s, lang) {
|
|
60242
|
-
const out2 = [];
|
|
60254
|
+
const out2 = [`> ${s.scoreboardNote}`, ""];
|
|
60243
60255
|
const orphans = unattributedFindings(result).filter((f) => !f.advisory).length;
|
|
60244
60256
|
if (orphans) out2.push(`> ${s.unattributed(orphans)}`, "");
|
|
60245
60257
|
if (derived.some((p) => p.basis === "attributed")) out2.push(`> ${s.sourceBasis}`, "");
|
|
@@ -60304,7 +60316,7 @@ function pagesComment(result, opts = {}) {
|
|
|
60304
60316
|
(a, b) => severityCount(b, "bloquant") - severityCount(a, "bloquant") || severityCount(b, "majeur") - severityCount(a, "majeur") || severityCount(b, "mineur") - severityCount(a, "mineur")
|
|
60305
60317
|
).map((p) => pageBlock(result, p, standard, lang)).filter((b) => b !== void 0);
|
|
60306
60318
|
const assemble = (nBlocks2, nRows2) => {
|
|
60307
|
-
const body3 = [...scoreboardTable(derived.slice(0, nRows2), s, lang), ""];
|
|
60319
|
+
const body3 = [...scoreboardTable(result, derived.slice(0, nRows2), standard, s, lang), ""];
|
|
60308
60320
|
if (nRows2 < derived.length) body3.push(s.scoreboardClamped(derived.length - nRows2), "");
|
|
60309
60321
|
body3.push(...basisCaveats(result, derived, s, lang));
|
|
60310
60322
|
if (redirected.length) body3.push(...renderRedirected(redirected, lang), "");
|
|
@@ -5,7 +5,7 @@ when_to_use: "Invoke WITHOUT being asked whenever changed front-end code should
|
|
|
5
5
|
allowed-tools: Bash(node ${CLAUDE_SKILL_DIR}/scripts/ultra11y.mjs *), Read, Grep, Glob
|
|
6
6
|
license: MIT
|
|
7
7
|
metadata:
|
|
8
|
-
version: 5.3.
|
|
8
|
+
version: 5.3.3
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# review-a11y — accessibility review of changed code
|
|
@@ -6,7 +6,7 @@ import { join as join50, relative as relative5, resolve as resolve15, sep as sep
|
|
|
6
6
|
import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
|
|
7
7
|
|
|
8
8
|
// src/types.ts
|
|
9
|
-
var VERSION = "5.3.
|
|
9
|
+
var VERSION = "5.3.3";
|
|
10
10
|
var SCHEMA_VERSION = 2;
|
|
11
11
|
|
|
12
12
|
// src/audit.ts
|
|
@@ -60058,10 +60058,14 @@ var S = {
|
|
|
60058
60058
|
clamped: (n) => `_${n} groupe(s) retir\xE9(s) de ce commentaire pour tenir dans la limite de GitHub \u2014 le r\xE9sum\xE9 de job les porte tous._`,
|
|
60059
60059
|
unanchored: (n) => `${n} constat(s) rattach\xE9(s) \xE0 une URL, sans ligne de code \xE0 annoter \u2014 voir le rapport.`,
|
|
60060
60060
|
unattributed: (n) => `${n} constat(s) ne sont rattach\xE9s \xE0 aucune page (code partag\xE9, fichier hors routes) \u2014 compt\xE9s dans l'audit global, jamais r\xE9partis d'office.`,
|
|
60061
|
-
sourceBasis: "Une page marqu\xE9e \xAB source \xBB n'a pas d'instantan\xE9 : l'absence de constat n'y vaut PAS conformit\xE9
|
|
60061
|
+
sourceBasis: "Une page marqu\xE9e \xAB source \xBB n'a pas d'instantan\xE9 : l'absence de constat n'y vaut PAS conformit\xE9. Ses crit\xE8res non d\xE9cid\xE9s restent \xAB \xE0 \xE9valuer \xBB, ils ne basculent jamais en conformes par silence.",
|
|
60062
60062
|
pageByPage: "page par page",
|
|
60063
60063
|
pagesCount: (n) => `${n} page(s)`,
|
|
60064
60064
|
testsCol: "Tests",
|
|
60065
|
+
conformingCol: "C",
|
|
60066
|
+
nonConformingCol: "NC",
|
|
60067
|
+
toAssessCol: "\xC0 \xE9valuer",
|
|
60068
|
+
scoreboardNote: "`C` conforme \xB7 `NC` non conforme \xB7 \xAB \xC0 \xE9valuer \xBB : personne ne l'a encore tranch\xE9 \u2014 ni conforme ni non conforme. Pas de pourcentage ici : un taux calcul\xE9 sur les seuls crit\xE8res d\xE9cid\xE9s se lit comme une note de page, et vaut la m\xEAme chose sur une bonne page que sur une mauvaise. Le taux et sa couverture sont dans la fiche par page de l'artefact.",
|
|
60065
60069
|
noPages: "Aucune page dans le p\xE9rim\xE8tre de ce run : le balayage n'a produit aucun instantan\xE9. Ce n'est pas un bilan vide, c'est un bilan absent \u2014 les crit\xE8res au rendu restent \xE0 \xE9valuer.",
|
|
60066
60070
|
pagesDetailNote: "Un bloc par page portant au moins une non-conformit\xE9, et seulement ses crit\xE8res **non conformes** \u2014 la grille compl\xE8te (tous les crit\xE8res de chaque page, avec leurs tests et leurs captures) vit dans l'artefact.",
|
|
60067
60071
|
pagesClamped: (n) => `_Le d\xE9tail de ${n} page(s) a \xE9t\xE9 retir\xE9 de ce commentaire pour tenir dans la limite de GitHub \u2014 l'artefact les porte toutes._`,
|
|
@@ -60099,10 +60103,14 @@ var S = {
|
|
|
60099
60103
|
clamped: (n) => `_${n} group(s) dropped from this comment to fit GitHub's limit \u2014 the job summary carries them all._`,
|
|
60100
60104
|
unanchored: (n) => `${n} finding(s) keyed to a URL, with no code line to annotate \u2014 see the report.`,
|
|
60101
60105
|
unattributed: (n) => `${n} finding(s) are attributed to no page (shared code, file outside any route) \u2014 counted in the overall audit, never spread across pages.`,
|
|
60102
|
-
sourceBasis: 'A page marked "source" has no snapshot: the absence of a finding there does NOT mean conforming
|
|
60106
|
+
sourceBasis: 'A page marked "source" has no snapshot: the absence of a finding there does NOT mean conforming. Its undecided criteria stay \u201Cto assess\u201D; they never turn conforming by silence.',
|
|
60103
60107
|
pageByPage: "page by page",
|
|
60104
60108
|
pagesCount: (n) => `${n} page(s)`,
|
|
60105
60109
|
testsCol: "Tests",
|
|
60110
|
+
conformingCol: "C",
|
|
60111
|
+
nonConformingCol: "NC",
|
|
60112
|
+
toAssessCol: "To assess",
|
|
60113
|
+
scoreboardNote: "`C` conforming \xB7 `NC` non-conforming \xB7 \u201CTo assess\u201D: nobody has ruled on it yet \u2014 neither conforming nor not. No percentage here: a rate over the decided criteria alone reads as a page score, and reads the same on a good page as on a bad one. The rate and its coverage live in the artifact's per-page sheet.",
|
|
60106
60114
|
noPages: "No page in this run's scope: the sweep produced no snapshot. This is not an empty scoreboard, it is a missing one \u2014 the rendering criteria stay to assess.",
|
|
60107
60115
|
pagesDetailNote: "One block per page carrying at least one non-conformity, and only its **non-conforming** criteria \u2014 the full grid (every criterion of every page, with its tests and its screenshot) lives in the artifact.",
|
|
60108
60116
|
pagesClamped: (n) => `_The detail of ${n} page(s) was dropped from this comment to fit GitHub's limit \u2014 the artifact carries them all._`,
|
|
@@ -60223,14 +60231,18 @@ function perPageTable(result, standard = CORE2, lang = "en") {
|
|
|
60223
60231
|
if (!scope.length) return "";
|
|
60224
60232
|
attributePages(result, scope);
|
|
60225
60233
|
const derived = derivePages(result, scope);
|
|
60226
|
-
return [`### ${s.perPage}`, "", ...scoreboardTable(derived, s, lang), "", ...basisCaveats(result, derived, s, lang)].join("\n");
|
|
60234
|
+
return [`### ${s.perPage}`, "", ...scoreboardTable(result, derived, standard, s, lang), "", ...basisCaveats(result, derived, s, lang)].join("\n");
|
|
60227
60235
|
}
|
|
60228
|
-
function scoreboardTable(derived, s, lang) {
|
|
60229
|
-
const out2 = [
|
|
60236
|
+
function scoreboardTable(result, derived, standard, s, lang) {
|
|
60237
|
+
const out2 = [
|
|
60238
|
+
`| ${s.page} | ${s.basis} | ${s.conformingCol} | ${s.nonConformingCol} | ${s.toAssessCol} | \u{1F534} | \u{1F7E0} | \u{1F7E1} |`,
|
|
60239
|
+
"| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: |"
|
|
60240
|
+
];
|
|
60230
60241
|
for (const pg of derived) {
|
|
60231
60242
|
const n = (sev) => severityCount(pg, sev);
|
|
60243
|
+
const t2 = pageTally(pageCriterionRows(result, pg, standard, lang));
|
|
60232
60244
|
out2.push(
|
|
60233
|
-
`| ${pg.name}${pg.auth ? " \u{1F512}" : ""} \u2014 \`${pg.url}\` | ${basisLabel(pg.basis, lang)} | ${
|
|
60245
|
+
`| ${pg.name}${pg.auth ? " \u{1F512}" : ""} \u2014 \`${pg.url}\` | ${basisLabel(pg.basis, lang)} | ${t2.c} | ${t2.nc} | ${t2.manual} | ${n("bloquant")} | ${n("majeur")} | ${n("mineur")} |`
|
|
60234
60246
|
);
|
|
60235
60247
|
}
|
|
60236
60248
|
return out2;
|
|
@@ -60239,7 +60251,7 @@ function severityCount(pg, sev) {
|
|
|
60239
60251
|
return pg.findings.filter((f) => !f.advisory && f.severity === sev).length;
|
|
60240
60252
|
}
|
|
60241
60253
|
function basisCaveats(result, derived, s, lang) {
|
|
60242
|
-
const out2 = [];
|
|
60254
|
+
const out2 = [`> ${s.scoreboardNote}`, ""];
|
|
60243
60255
|
const orphans = unattributedFindings(result).filter((f) => !f.advisory).length;
|
|
60244
60256
|
if (orphans) out2.push(`> ${s.unattributed(orphans)}`, "");
|
|
60245
60257
|
if (derived.some((p) => p.basis === "attributed")) out2.push(`> ${s.sourceBasis}`, "");
|
|
@@ -60304,7 +60316,7 @@ function pagesComment(result, opts = {}) {
|
|
|
60304
60316
|
(a, b) => severityCount(b, "bloquant") - severityCount(a, "bloquant") || severityCount(b, "majeur") - severityCount(a, "majeur") || severityCount(b, "mineur") - severityCount(a, "mineur")
|
|
60305
60317
|
).map((p) => pageBlock(result, p, standard, lang)).filter((b) => b !== void 0);
|
|
60306
60318
|
const assemble = (nBlocks2, nRows2) => {
|
|
60307
|
-
const body3 = [...scoreboardTable(derived.slice(0, nRows2), s, lang), ""];
|
|
60319
|
+
const body3 = [...scoreboardTable(result, derived.slice(0, nRows2), standard, s, lang), ""];
|
|
60308
60320
|
if (nRows2 < derived.length) body3.push(s.scoreboardClamped(derived.length - nRows2), "");
|
|
60309
60321
|
body3.push(...basisCaveats(result, derived, s, lang));
|
|
60310
60322
|
if (redirected.length) body3.push(...renderRedirected(redirected, lang), "");
|
package/skills/ultra11y/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: "Use to AUDIT existing HTML/CSS/JSX against WCAG 2.2 AA accessibili
|
|
|
4
4
|
when_to_use: "Invoke when the user asks for an accessibility AUDIT or a formal deliverable: audit a repo, site or page against WCAG 2.2 AA or a country standard (RGAA, Section 508, EN 301 549); produce a dated conformance report, a criterion grid, a PRD backlog, or tickets (GitHub/GitLab/Jira); author accessible markup from scratch; wire the repo gate (init --hook/--ci) or a rendered-DOM capture pipeline. For a review of the code UNDER CHANGE (diff, branch, PR), use the `review-a11y` skill instead."
|
|
5
5
|
license: MIT
|
|
6
6
|
metadata:
|
|
7
|
-
version: 5.3.
|
|
7
|
+
version: 5.3.3
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# ultra11y — audit WCAG 2.2 AA and write accessible markup
|
|
@@ -6,7 +6,7 @@ import { join as join50, relative as relative5, resolve as resolve15, sep as sep
|
|
|
6
6
|
import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
|
|
7
7
|
|
|
8
8
|
// src/types.ts
|
|
9
|
-
var VERSION = "5.3.
|
|
9
|
+
var VERSION = "5.3.3";
|
|
10
10
|
var SCHEMA_VERSION = 2;
|
|
11
11
|
|
|
12
12
|
// src/audit.ts
|
|
@@ -60058,10 +60058,14 @@ var S = {
|
|
|
60058
60058
|
clamped: (n) => `_${n} groupe(s) retir\xE9(s) de ce commentaire pour tenir dans la limite de GitHub \u2014 le r\xE9sum\xE9 de job les porte tous._`,
|
|
60059
60059
|
unanchored: (n) => `${n} constat(s) rattach\xE9(s) \xE0 une URL, sans ligne de code \xE0 annoter \u2014 voir le rapport.`,
|
|
60060
60060
|
unattributed: (n) => `${n} constat(s) ne sont rattach\xE9s \xE0 aucune page (code partag\xE9, fichier hors routes) \u2014 compt\xE9s dans l'audit global, jamais r\xE9partis d'office.`,
|
|
60061
|
-
sourceBasis: "Une page marqu\xE9e \xAB source \xBB n'a pas d'instantan\xE9 : l'absence de constat n'y vaut PAS conformit\xE9
|
|
60061
|
+
sourceBasis: "Une page marqu\xE9e \xAB source \xBB n'a pas d'instantan\xE9 : l'absence de constat n'y vaut PAS conformit\xE9. Ses crit\xE8res non d\xE9cid\xE9s restent \xAB \xE0 \xE9valuer \xBB, ils ne basculent jamais en conformes par silence.",
|
|
60062
60062
|
pageByPage: "page par page",
|
|
60063
60063
|
pagesCount: (n) => `${n} page(s)`,
|
|
60064
60064
|
testsCol: "Tests",
|
|
60065
|
+
conformingCol: "C",
|
|
60066
|
+
nonConformingCol: "NC",
|
|
60067
|
+
toAssessCol: "\xC0 \xE9valuer",
|
|
60068
|
+
scoreboardNote: "`C` conforme \xB7 `NC` non conforme \xB7 \xAB \xC0 \xE9valuer \xBB : personne ne l'a encore tranch\xE9 \u2014 ni conforme ni non conforme. Pas de pourcentage ici : un taux calcul\xE9 sur les seuls crit\xE8res d\xE9cid\xE9s se lit comme une note de page, et vaut la m\xEAme chose sur une bonne page que sur une mauvaise. Le taux et sa couverture sont dans la fiche par page de l'artefact.",
|
|
60065
60069
|
noPages: "Aucune page dans le p\xE9rim\xE8tre de ce run : le balayage n'a produit aucun instantan\xE9. Ce n'est pas un bilan vide, c'est un bilan absent \u2014 les crit\xE8res au rendu restent \xE0 \xE9valuer.",
|
|
60066
60070
|
pagesDetailNote: "Un bloc par page portant au moins une non-conformit\xE9, et seulement ses crit\xE8res **non conformes** \u2014 la grille compl\xE8te (tous les crit\xE8res de chaque page, avec leurs tests et leurs captures) vit dans l'artefact.",
|
|
60067
60071
|
pagesClamped: (n) => `_Le d\xE9tail de ${n} page(s) a \xE9t\xE9 retir\xE9 de ce commentaire pour tenir dans la limite de GitHub \u2014 l'artefact les porte toutes._`,
|
|
@@ -60099,10 +60103,14 @@ var S = {
|
|
|
60099
60103
|
clamped: (n) => `_${n} group(s) dropped from this comment to fit GitHub's limit \u2014 the job summary carries them all._`,
|
|
60100
60104
|
unanchored: (n) => `${n} finding(s) keyed to a URL, with no code line to annotate \u2014 see the report.`,
|
|
60101
60105
|
unattributed: (n) => `${n} finding(s) are attributed to no page (shared code, file outside any route) \u2014 counted in the overall audit, never spread across pages.`,
|
|
60102
|
-
sourceBasis: 'A page marked "source" has no snapshot: the absence of a finding there does NOT mean conforming
|
|
60106
|
+
sourceBasis: 'A page marked "source" has no snapshot: the absence of a finding there does NOT mean conforming. Its undecided criteria stay \u201Cto assess\u201D; they never turn conforming by silence.',
|
|
60103
60107
|
pageByPage: "page by page",
|
|
60104
60108
|
pagesCount: (n) => `${n} page(s)`,
|
|
60105
60109
|
testsCol: "Tests",
|
|
60110
|
+
conformingCol: "C",
|
|
60111
|
+
nonConformingCol: "NC",
|
|
60112
|
+
toAssessCol: "To assess",
|
|
60113
|
+
scoreboardNote: "`C` conforming \xB7 `NC` non-conforming \xB7 \u201CTo assess\u201D: nobody has ruled on it yet \u2014 neither conforming nor not. No percentage here: a rate over the decided criteria alone reads as a page score, and reads the same on a good page as on a bad one. The rate and its coverage live in the artifact's per-page sheet.",
|
|
60106
60114
|
noPages: "No page in this run's scope: the sweep produced no snapshot. This is not an empty scoreboard, it is a missing one \u2014 the rendering criteria stay to assess.",
|
|
60107
60115
|
pagesDetailNote: "One block per page carrying at least one non-conformity, and only its **non-conforming** criteria \u2014 the full grid (every criterion of every page, with its tests and its screenshot) lives in the artifact.",
|
|
60108
60116
|
pagesClamped: (n) => `_The detail of ${n} page(s) was dropped from this comment to fit GitHub's limit \u2014 the artifact carries them all._`,
|
|
@@ -60223,14 +60231,18 @@ function perPageTable(result, standard = CORE2, lang = "en") {
|
|
|
60223
60231
|
if (!scope.length) return "";
|
|
60224
60232
|
attributePages(result, scope);
|
|
60225
60233
|
const derived = derivePages(result, scope);
|
|
60226
|
-
return [`### ${s.perPage}`, "", ...scoreboardTable(derived, s, lang), "", ...basisCaveats(result, derived, s, lang)].join("\n");
|
|
60234
|
+
return [`### ${s.perPage}`, "", ...scoreboardTable(result, derived, standard, s, lang), "", ...basisCaveats(result, derived, s, lang)].join("\n");
|
|
60227
60235
|
}
|
|
60228
|
-
function scoreboardTable(derived, s, lang) {
|
|
60229
|
-
const out2 = [
|
|
60236
|
+
function scoreboardTable(result, derived, standard, s, lang) {
|
|
60237
|
+
const out2 = [
|
|
60238
|
+
`| ${s.page} | ${s.basis} | ${s.conformingCol} | ${s.nonConformingCol} | ${s.toAssessCol} | \u{1F534} | \u{1F7E0} | \u{1F7E1} |`,
|
|
60239
|
+
"| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: |"
|
|
60240
|
+
];
|
|
60230
60241
|
for (const pg of derived) {
|
|
60231
60242
|
const n = (sev) => severityCount(pg, sev);
|
|
60243
|
+
const t2 = pageTally(pageCriterionRows(result, pg, standard, lang));
|
|
60232
60244
|
out2.push(
|
|
60233
|
-
`| ${pg.name}${pg.auth ? " \u{1F512}" : ""} \u2014 \`${pg.url}\` | ${basisLabel(pg.basis, lang)} | ${
|
|
60245
|
+
`| ${pg.name}${pg.auth ? " \u{1F512}" : ""} \u2014 \`${pg.url}\` | ${basisLabel(pg.basis, lang)} | ${t2.c} | ${t2.nc} | ${t2.manual} | ${n("bloquant")} | ${n("majeur")} | ${n("mineur")} |`
|
|
60234
60246
|
);
|
|
60235
60247
|
}
|
|
60236
60248
|
return out2;
|
|
@@ -60239,7 +60251,7 @@ function severityCount(pg, sev) {
|
|
|
60239
60251
|
return pg.findings.filter((f) => !f.advisory && f.severity === sev).length;
|
|
60240
60252
|
}
|
|
60241
60253
|
function basisCaveats(result, derived, s, lang) {
|
|
60242
|
-
const out2 = [];
|
|
60254
|
+
const out2 = [`> ${s.scoreboardNote}`, ""];
|
|
60243
60255
|
const orphans = unattributedFindings(result).filter((f) => !f.advisory).length;
|
|
60244
60256
|
if (orphans) out2.push(`> ${s.unattributed(orphans)}`, "");
|
|
60245
60257
|
if (derived.some((p) => p.basis === "attributed")) out2.push(`> ${s.sourceBasis}`, "");
|
|
@@ -60304,7 +60316,7 @@ function pagesComment(result, opts = {}) {
|
|
|
60304
60316
|
(a, b) => severityCount(b, "bloquant") - severityCount(a, "bloquant") || severityCount(b, "majeur") - severityCount(a, "majeur") || severityCount(b, "mineur") - severityCount(a, "mineur")
|
|
60305
60317
|
).map((p) => pageBlock(result, p, standard, lang)).filter((b) => b !== void 0);
|
|
60306
60318
|
const assemble = (nBlocks2, nRows2) => {
|
|
60307
|
-
const body3 = [...scoreboardTable(derived.slice(0, nRows2), s, lang), ""];
|
|
60319
|
+
const body3 = [...scoreboardTable(result, derived.slice(0, nRows2), standard, s, lang), ""];
|
|
60308
60320
|
if (nRows2 < derived.length) body3.push(s.scoreboardClamped(derived.length - nRows2), "");
|
|
60309
60321
|
body3.push(...basisCaveats(result, derived, s, lang));
|
|
60310
60322
|
if (redirected.length) body3.push(...renderRedirected(redirected, lang), "");
|
package/src/annotate.ts
CHANGED
|
@@ -125,10 +125,15 @@ const S = {
|
|
|
125
125
|
unattributed: (n: number) =>
|
|
126
126
|
`${n} constat(s) ne sont rattachés à aucune page (code partagé, fichier hors routes) — comptés dans l'audit global, jamais répartis d'office.`,
|
|
127
127
|
sourceBasis:
|
|
128
|
-
"Une page marquée « source » n'a pas d'instantané : l'absence de constat n'y vaut PAS conformit
|
|
128
|
+
"Une page marquée « source » n'a pas d'instantané : l'absence de constat n'y vaut PAS conformité. Ses critères non décidés restent « à évaluer », ils ne basculent jamais en conformes par silence.",
|
|
129
129
|
pageByPage: "page par page",
|
|
130
130
|
pagesCount: (n: number) => `${n} page(s)`,
|
|
131
131
|
testsCol: "Tests",
|
|
132
|
+
conformingCol: "C",
|
|
133
|
+
nonConformingCol: "NC",
|
|
134
|
+
toAssessCol: "À évaluer",
|
|
135
|
+
scoreboardNote:
|
|
136
|
+
"`C` conforme · `NC` non conforme · « À évaluer » : personne ne l'a encore tranché — ni conforme ni non conforme. Pas de pourcentage ici : un taux calculé sur les seuls critères décidés se lit comme une note de page, et vaut la même chose sur une bonne page que sur une mauvaise. Le taux et sa couverture sont dans la fiche par page de l'artefact.",
|
|
132
137
|
noPages:
|
|
133
138
|
"Aucune page dans le périmètre de ce run : le balayage n'a produit aucun instantané. Ce n'est pas un bilan vide, c'est un bilan absent — les critères au rendu restent à évaluer.",
|
|
134
139
|
pagesDetailNote:
|
|
@@ -173,10 +178,15 @@ const S = {
|
|
|
173
178
|
unattributed: (n: number) =>
|
|
174
179
|
`${n} finding(s) are attributed to no page (shared code, file outside any route) — counted in the overall audit, never spread across pages.`,
|
|
175
180
|
sourceBasis:
|
|
176
|
-
'A page marked "source" has no snapshot: the absence of a finding there does NOT mean conforming
|
|
181
|
+
'A page marked "source" has no snapshot: the absence of a finding there does NOT mean conforming. Its undecided criteria stay “to assess”; they never turn conforming by silence.',
|
|
177
182
|
pageByPage: "page by page",
|
|
178
183
|
pagesCount: (n: number) => `${n} page(s)`,
|
|
179
184
|
testsCol: "Tests",
|
|
185
|
+
conformingCol: "C",
|
|
186
|
+
nonConformingCol: "NC",
|
|
187
|
+
toAssessCol: "To assess",
|
|
188
|
+
scoreboardNote:
|
|
189
|
+
"`C` conforming · `NC` non-conforming · “To assess”: nobody has ruled on it yet — neither conforming nor not. No percentage here: a rate over the decided criteria alone reads as a page score, and reads the same on a good page as on a bad one. The rate and its coverage live in the artifact's per-page sheet.",
|
|
180
190
|
noPages:
|
|
181
191
|
"No page in this run's scope: the sweep produced no snapshot. This is not an empty scoreboard, it is a missing one — the rendering criteria stay to assess.",
|
|
182
192
|
pagesDetailNote:
|
|
@@ -384,18 +394,34 @@ export function perPageTable(result: AuditResult, standard: StandardId = CORE, l
|
|
|
384
394
|
attributePages(result, scope);
|
|
385
395
|
const derived = derivePages(result, scope);
|
|
386
396
|
// The summary has a 1 MiB budget: it never clamps, and passes every page.
|
|
387
|
-
return [`### ${s.perPage}`, "", ...scoreboardTable(derived, s, lang), "", ...basisCaveats(result, derived, s, lang)].join("\n");
|
|
397
|
+
return [`### ${s.perPage}`, "", ...scoreboardTable(result, derived, standard, s, lang), "", ...basisCaveats(result, derived, s, lang)].join("\n");
|
|
388
398
|
}
|
|
389
399
|
|
|
390
400
|
/** The scoreboard's rows. Extracted so the job summary and the page-by-page pull-request
|
|
391
401
|
* comment draw ONE table from one projection — a second copy of this loop is a second chance
|
|
392
|
-
* to disagree about what a page's rate is.
|
|
393
|
-
|
|
394
|
-
|
|
402
|
+
* to disagree about what a page's rate is.
|
|
403
|
+
*
|
|
404
|
+
* COUNTS, not a percentage. Every status comes from `pageCriterionRows`, in the ACTIVE
|
|
405
|
+
* standard's vocabulary — `PageResult.conformancePct` is always WCAG-keyed and would report a
|
|
406
|
+
* pack page against the core's 55.
|
|
407
|
+
*
|
|
408
|
+
* A percentage was worse than useless here. When the judgment criteria are undecided — which
|
|
409
|
+
* is the NORMAL state without an adjudication pass, and the state a rejected one falls back to
|
|
410
|
+
* — a page has 2 conforming and 2 non-conforming out of 106, and the cell read « 50 % (4/106) »
|
|
411
|
+
* on every row. Half of four criteria is not half a page, but the eye reads a page score, and
|
|
412
|
+
* it read the same score for a good page and a bad one. The three counts cannot be misread:
|
|
413
|
+
* what conforms, what does not, and how much nobody has ruled on yet. The percentage stays in
|
|
414
|
+
* the artifact's per-page sheet, next to the coverage sentence that qualifies it. */
|
|
415
|
+
function scoreboardTable(result: AuditResult, derived: PageResult[], standard: StandardId, s: (typeof S)[Lang], lang: Lang): string[] {
|
|
416
|
+
const out: string[] = [
|
|
417
|
+
`| ${s.page} | ${s.basis} | ${s.conformingCol} | ${s.nonConformingCol} | ${s.toAssessCol} | 🔴 | 🟠 | 🟡 |`,
|
|
418
|
+
"| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: |",
|
|
419
|
+
];
|
|
395
420
|
for (const pg of derived) {
|
|
396
421
|
const n = (sev: Severity): number => severityCount(pg, sev);
|
|
422
|
+
const t = pageTally(pageCriterionRows(result, pg, standard, lang));
|
|
397
423
|
out.push(
|
|
398
|
-
`| ${pg.name}${pg.auth ? " 🔒" : ""} — \`${pg.url}\` | ${basisLabel(pg.basis, lang)} | ${
|
|
424
|
+
`| ${pg.name}${pg.auth ? " 🔒" : ""} — \`${pg.url}\` | ${basisLabel(pg.basis, lang)} | ${t.c} | ${t.nc} | ${t.manual} | ${n("bloquant")} | ${n("majeur")} | ${n("mineur")} |`,
|
|
399
425
|
);
|
|
400
426
|
}
|
|
401
427
|
return out;
|
|
@@ -409,7 +435,8 @@ function severityCount(pg: PageResult, sev: Severity): number {
|
|
|
409
435
|
* than a snapshot actually present. One caveat per basis, from the shared sentences — a
|
|
410
436
|
* « non audité » page must not be explained by the note that asserts it has no snapshot. */
|
|
411
437
|
function basisCaveats(result: AuditResult, derived: PageResult[], s: (typeof S)[Lang], lang: Lang): string[] {
|
|
412
|
-
|
|
438
|
+
// What the three count columns mean, and why there is no percentage beside them.
|
|
439
|
+
const out: string[] = [`> ${s.scoreboardNote}`, ""];
|
|
413
440
|
const orphans = unattributedFindings(result).filter((f) => !f.advisory).length;
|
|
414
441
|
if (orphans) out.push(`> ${s.unattributed(orphans)}`, "");
|
|
415
442
|
if (derived.some((p) => p.basis === "attributed")) out.push(`> ${s.sourceBasis}`, "");
|
|
@@ -520,7 +547,7 @@ export function pagesComment(result: AuditResult, opts: AnnotateOptions & { runU
|
|
|
520
547
|
// goes first because the scoreboard is the half that cannot be reconstructed from the
|
|
521
548
|
// artifact link alone.
|
|
522
549
|
const assemble = (nBlocks: number, nRows: number): string => {
|
|
523
|
-
const body: string[] = [...scoreboardTable(derived.slice(0, nRows), s, lang), ""];
|
|
550
|
+
const body: string[] = [...scoreboardTable(result, derived.slice(0, nRows), standard, s, lang), ""];
|
|
524
551
|
if (nRows < derived.length) body.push(s.scoreboardClamped(derived.length - nRows), "");
|
|
525
552
|
body.push(...basisCaveats(result, derived, s, lang));
|
|
526
553
|
if (redirected.length) body.push(...renderRedirected(redirected, lang), "");
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Single source of truth for shared types + the embedded version. sync-version.mjs
|
|
2
2
|
// rewrites VERSION at release time (kept in lockstep with package.json + SKILL.md).
|
|
3
|
-
export const VERSION = "5.3.
|
|
3
|
+
export const VERSION = "5.3.3";
|
|
4
4
|
export const SCHEMA_VERSION = 2;
|
|
5
5
|
|
|
6
6
|
// Lang = the UI FRAME's language — the `L` localization tables each module (report,
|