PR: Editorial shape + one-truth design system (render-as-html) #42

Branch editorial-shape-one-truthmain
Commits 15
Files 3 changed
Diff size +547 / −95
Status merged
Risk surface
Touches the canonical design-system doc (SKILL.md) — changes here define what every downstream artifact renders against.
Touches the dogfood gallery (index.html) — the in-browser component reference used to validate all shape contracts.
Findings:
Critical 1
Warning 2
Nit 2
All resolved before merge

SKILL.md

Color subsection — palette migration
@@ -312,12 +312,20 @@ ### Color
312312 ```
313313 Light:
314--accent #c74438 coral — THE single accent
315--paper #fafaf7 cool white — page background
314+--paper #faf6ef warm cream — page background
315+--paper-tint #f3ede1 recessed surfaces, hover fills
316+--paper-card #fbf7f0 raised cards
317+--accent #8a3a1a terracotta — THE single accent (links, active state, emphasis)
318+--accent-2 #c2901a ochre/gold — affirmative actions only (primary button, star/select on)
316319 --ok #2f7d44
317320 --warn #9b641d
318321 ```
Warning Coral #c74438 removed; terracotta #8a3a1a + ochre #c2901a take over. Cool white paper also replaced by warm cream. All existing artifacts using the old hex values will render out-of-palette until regenerated.

index.html

:root — value swap, variable names preserved (no-rename constraint)
@@ -14,16 +14,16 @@ :root block
1414 :root {
15 --paper: #fafaf7; /* cool white */
15+ --paper: #faf6ef; /* warm cream */
16 --paper-tint: #f5f5f2;
16+ --paper-tint: #f3ede1;
17 --accent: #c74438; /* coral */
17+ --accent: #8a3a1a; /* terracotta */
18+ --accent-2: #c2901a; /* ochre */
1819 --ink: #1a1815;
1920 --ink-soft: #4a443c;
2021 --muted: #8a8378;
2122 }
Nit Variable names kept stable (per the no-rename constraint) — only hex values changed. The plan's literal diff-gate check would have failed once additive markup arrived; see Warning finding #2 below.

index.html

In-text search — stateful regex bug & fix (Critical)
@@ -412,22 +412,20 @@ highlightMatches() — in-text search implementation
412412 function highlightMatches(q) {
413413 const esc = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
414 const rx = new RegExp(esc(q), 'gi');
415414 nodes.forEach(el => {
416 if (rx.test(el.dataset.original)) {
417 el.innerHTML = el.dataset.original.replace(rx, m =>
418 `<mark>${m}</mark>`);
419 }
415+ const rx = new RegExp(esc(q), 'gi'); // fresh per node — no lastIndex drift
416+ el.innerHTML = el.dataset.original.replace(rx, m =>
417+ `<mark>${m}</mark>`);
420418 });
421419 }
Critical A single RegExp with the g flag, called via .test() then .replace() across multiple DOM nodes, advances lastIndex on each test(). After a match, the next test() starts mid-string and may skip a valid match. Fix: construct a fresh RegExp inside the forEach loop, eliminating shared stateful state. The if (rx.test(…)) guard is also dropped — replace is a no-op on zero matches and the guard was only there to avoid replacing on non-matching nodes, which the stateful regex was causing.

index.html

Cleanup — dead data-sec attributes + deduped .cl-export
@@ -198,8 +198,5 @@ Stale data-sec attrs removed
198<section data-sec="color" id="sec-color">
198+<section id="sec-color">
199<section data-sec="type" id="sec-type">
199+<section id="sec-type">
200200 <section id="sec-layout">
Nit data-sec was referenced by an old JS section-filter that was replaced — attributes are now dead weight. Removed across all 14 section elements. No behavior change.

All findings

Critical Stateful global regex skips matches across nodes index.html:414–419

A single RegExp(esc(q), 'gi') was constructed outside the forEach loop. Because g-flag regexes track lastIndex, calling rx.test(el.dataset.original) advances the cursor. The subsequent rx.replace(…) call then starts mid-string on the next node — silently skipping any match that falls before the advanced offset.

Why it matters: Search appeared to work on small documents but silently missed every other match on long pages with many nodes sharing the same search term. Impossible to debug by inspection; only caught via systematic diff review.
✓ fixed
Warning Plan's no-rename diff gate was unsatisfiable once additive markup landed SKILL.md — plan gate

The plan specified a literal diff gate: "variable names must not change." Once the PR added --accent-2 and --paper-card (both new, not renamed), the gate tripped on every CI pass even though the constraint was satisfied — no existing variable was renamed.

Why it matters: Binary diff-text gates break on additive changes. The correct invariant is set-membership: "every variable name present in the old :root must appear in the new :root." Resolved by updating the gate logic to check the variable-name set rather than the raw line diff.
✓ resolved
Warning Design-system intro contradicted the new two-register truth SKILL.md:276–280

After the register table was added (Reading vs Instrument), the introductory paragraph in the Design system section still described a single-palette approach without acknowledging the two registers. A reader skimming the intro would miss that the palette is applied differently by shape.

Why it matters: SKILL.md is the canonical doc read by every downstream render agent. Contradictory framing at the intro guarantees that fast readers get the wrong mental model and have to reconcile it from context later. Fixed by rewriting the intro sentence to lead with the two-register split.
✓ fixed
Nit Dead data-sec attributes on all section elements index.html:198–340 (14 elements)

data-sec="…" was used by an old JS section-filter that was replaced in a prior commit. The attributes remained on all 14 <section> elements — harmless but misleading noise in the DOM.

Why it matters: Dead attributes invite copy-paste into new sections, accumulating forever. Removed across all 14 elements.
✓ removed
Nit .cl-export byte-identical to .cap-output index.html:~520 (CSS)

.cl-export was added to style the copy-prompt textarea, but its declaration block was byte-for-byte identical to the pre-existing .cap-output class. Both set the same font, size, padding, border, background, and resize values.

Why it matters: Two names for one visual thing means future tweaks happen to one and not the other. Deduped to a single selector with a comma rule: .cap-output, .cl-export { … }.
✓ deduped

Review summary

Commits reviewed 15 editorial-shape-one-truth → main
Files changed 3 SKILL.md, README.md, index.html
Net diff +547 −95 lines removed
Findings 5 1 critical · 2 warning · 2 nit
All 5 findings resolved before merge. Ship it.