@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 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: "axe scan is in the gate; confirm the checklist items beyond the scan (focus order, live regions)",
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.4",
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"
@@ -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-04 · **Status:** active
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-04 # recommended — enables standard-version drift tracking (#compliance)
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