@piercebarney/whs-eleventy 2026.10.4 → 2026.10.6
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/lib/compliance.js +51 -1
- package/package.json +1 -1
- package/standard/CHANGELOG.md +47 -0
- package/standard/core.md +2 -2
package/lib/compliance.js
CHANGED
|
@@ -51,6 +51,37 @@ function walk(dir) {
|
|
|
51
51
|
return out;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
// Mask out every @media (prefers-reduced-motion: no-preference) { ... }
|
|
55
|
+
// block's entire contents (string search + brace-counting to find the
|
|
56
|
+
// matching close, so nested rulesets inside the media block are correctly
|
|
57
|
+
// included in what gets masked) — whatever remains is, by construction,
|
|
58
|
+
// outside the required gate.
|
|
59
|
+
function maskGatedMotionBlocks(css) {
|
|
60
|
+
const marker = /@media[^{]*prefers-reduced-motion\s*:\s*no-preference[^{]*\{/;
|
|
61
|
+
let out = "";
|
|
62
|
+
let rest = css;
|
|
63
|
+
let m;
|
|
64
|
+
while ((m = rest.match(marker))) {
|
|
65
|
+
let depth = 1;
|
|
66
|
+
let i = m.index + m[0].length;
|
|
67
|
+
while (i < rest.length && depth > 0) {
|
|
68
|
+
if (rest[i] === "{") depth++;
|
|
69
|
+
else if (rest[i] === "}") depth--;
|
|
70
|
+
i++;
|
|
71
|
+
}
|
|
72
|
+
out += rest.slice(0, m.index);
|
|
73
|
+
rest = rest.slice(i);
|
|
74
|
+
}
|
|
75
|
+
return out + rest;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// (?<![\w-]) rather than a bare \b: a custom property like --pico-transition:
|
|
79
|
+
// is not a transition *declaration* — \b alone still matches right after the
|
|
80
|
+
// hyphen in "-transition", which false-positives on exactly that shape (found
|
|
81
|
+
// in the reference template's own vendored pico.min.css).
|
|
82
|
+
const MOTION_PROP = /(?<![\w-])(transition|animation|scroll-behavior)\s*:/;
|
|
83
|
+
const hasUngatedMotion = (css) => MOTION_PROP.test(maskGatedMotionBlocks(css));
|
|
84
|
+
|
|
54
85
|
// grep the tracked source tree (not _site, not node_modules).
|
|
55
86
|
function grepSrc(re, exts = [".js", ".njk", ".css", ".json"]) {
|
|
56
87
|
const roots = ["src", "static", "scripts"].map((d) => path.join(ROOT, d));
|
|
@@ -594,9 +625,28 @@ const CHECKS = {
|
|
|
594
625
|
a11y: () => {
|
|
595
626
|
if (!/\ba11y\b/.test(JSON.parse(read("package.json") || "{}").scripts?.check || ""))
|
|
596
627
|
return { status: FAIL, note: "no a11y stage in the gate" };
|
|
628
|
+
// Scan the authored source tree (src/static/scripts), not built _site —
|
|
629
|
+
// a passthrough-copied third-party stylesheet (e.g. a CSS framework
|
|
630
|
+
// vendored via addPassthroughCopy) lands in _site but was never authored
|
|
631
|
+
// by, or gate-able by, this project; scanning it produced a false FAIL
|
|
632
|
+
// against the reference template's own vendored pico.min.css.
|
|
633
|
+
const cssFiles = ["src", "static", "scripts"]
|
|
634
|
+
.map((d) => path.join(ROOT, d))
|
|
635
|
+
.flatMap((r) => walk(r))
|
|
636
|
+
.filter((f) => f.endsWith(".css"));
|
|
637
|
+
const ungated = cssFiles.filter((f) => hasUngatedMotion(fs.readFileSync(f, "utf8")));
|
|
638
|
+
if (ungated.length)
|
|
639
|
+
return {
|
|
640
|
+
status: FAIL,
|
|
641
|
+
note: `transition/animation not gated behind prefers-reduced-motion in ${ungated
|
|
642
|
+
.map((f) => path.relative(ROOT, f))
|
|
643
|
+
.join(", ")}`,
|
|
644
|
+
};
|
|
597
645
|
return {
|
|
598
646
|
status: MANUAL,
|
|
599
|
-
note:
|
|
647
|
+
note:
|
|
648
|
+
"axe scan is in the gate; prefers-reduced-motion gating confirmed; " +
|
|
649
|
+
"confirm remaining checklist items beyond the scan (focus order, live regions)",
|
|
600
650
|
};
|
|
601
651
|
},
|
|
602
652
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@piercebarney/whs-eleventy",
|
|
3
|
-
"version": "2026.10.
|
|
3
|
+
"version": "2026.10.6",
|
|
4
4
|
"description": "The web house style's Eleventy + Netlify tooling — the compliance sweep, the infra doctor, the link/CSP integrity check, and the a11y scan, shared by every project on the stack.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"whs": "cli.js"
|
package/standard/CHANGELOG.md
CHANGED
|
@@ -21,6 +21,53 @@ enforced, new *opt-in* chapters/capabilities, tooling-only changes — is
|
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
24
|
+
## 2026-10-06 [breaking]
|
|
25
|
+
|
|
26
|
+
### packages/whs-eleventy
|
|
27
|
+
|
|
28
|
+
`a11y`'s chapter in `lib/compliance.js` was 100% unconditional `MANUAL` — it
|
|
29
|
+
never actually checked any of the Contract specs it listed. This adds real
|
|
30
|
+
mechanization for one of them: `core.md#a11y` requires every transition/
|
|
31
|
+
animation to be gated behind `@media (prefers-reduced-motion: no-preference)`
|
|
32
|
+
(also called out in `core.md`'s own anti-patterns list). Found via a real
|
|
33
|
+
project's manual anti-patterns review catching a violation the compliance
|
|
34
|
+
sweep itself missed. The check scans authored source (`src/static/scripts`),
|
|
35
|
+
not built output, so vendored/passthrough-copied third-party CSS a project
|
|
36
|
+
doesn't author is correctly excluded. Unlike this week's other silent-pass
|
|
37
|
+
fixes, this doesn't just correct a check that had a redundant real backstop
|
|
38
|
+
elsewhere (`client-logic`, 2026-10-03) or a pure-doc mismatch with no
|
|
39
|
+
enforcement (`forms`, 2026-10-04) — this is genuinely new detection where
|
|
40
|
+
none existed before. That means it can flip an already-`MANUAL`-passing real
|
|
41
|
+
project's `a11y` status to `FAIL` on its very next compliance run, if that
|
|
42
|
+
project has real, ungated transition/animation CSS in its own authored
|
|
43
|
+
source — not a bug in this fix, but the intended, working check finally
|
|
44
|
+
running. Known, narrow limitation: vendor-prefixed declarations
|
|
45
|
+
(`-webkit-transition:` etc.) aren't detected — an acceptable, low-probability
|
|
46
|
+
gap, consistent with other crude-but-honest heuristics already in this file.
|
|
47
|
+
No `core.md` chapter changed — `packages/whs-eleventy`-only. No `feedback/`
|
|
48
|
+
file. Fixed in `6576a04`.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 2026-10-05 [non-breaking]
|
|
53
|
+
|
|
54
|
+
### templates/commands
|
|
55
|
+
|
|
56
|
+
`/whs:adopt`'s step 5 npm-scripts proposal only ever covered
|
|
57
|
+
`check`/`compliance`/`status`, never `build`/`serve` — a real adopting
|
|
58
|
+
project ended up with no `npm run serve` at all and had to be told the
|
|
59
|
+
convention rather than adoption ever mentioning it. The step now also
|
|
60
|
+
proposes `build`/`serve`, using `templates/eleventy-netlify/package.json`'s
|
|
61
|
+
exact commands, with a per-script-name conflict check (not the all-or-nothing
|
|
62
|
+
check the adjacent proposal uses) — a pre-existing project is more likely to
|
|
63
|
+
already have some build/serve mechanism, possibly under a different name,
|
|
64
|
+
than to already have WHS-specific scripts, so each of the two names is
|
|
65
|
+
checked and proposed independently, and the human is told plainly to confirm
|
|
66
|
+
before treating either as net-new. Fourth `/whs:adopt`-vs-`/whs:new` parity
|
|
67
|
+
gap found this week. No `core.md` chapter changed. Fixed in `41f65c3`.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
24
71
|
## 2026-10-04 [non-breaking]
|
|
25
72
|
|
|
26
73
|
### stacks/eleventy-netlify.md
|
package/standard/core.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Web project house style — CORE (stack-agnostic)
|
|
2
2
|
|
|
3
|
-
**Version:** 2026-10-
|
|
3
|
+
**Version:** 2026-10-06 · **Status:** active
|
|
4
4
|
|
|
5
5
|
This is the stack-agnostic contract every web project follows, regardless of
|
|
6
6
|
framework, host, or CSS system. It says **what** must be true, with concrete
|
|
@@ -1467,7 +1467,7 @@ governs it and follow that chapter — the slugs are the index (a `<script>` →
|
|
|
1467
1467
|
- production-url: https://example.com
|
|
1468
1468
|
- content-type: tool # tool | article (article => feeds)
|
|
1469
1469
|
- publishing-rate: ~5 pages/week
|
|
1470
|
-
- standard-version: 2026-10-
|
|
1470
|
+
- standard-version: 2026-10-06 # recommended — enables standard-version drift tracking (#compliance)
|
|
1471
1471
|
```
|
|
1472
1472
|
|
|
1473
1473
|
If the section is absent, the assistant's first action is to run the flow above
|