Hover capture via forced pseudo-state, text-wrap support, per-viewport video source re-selection, svg paint recovery, semantic tokens & section naming

Capture:
- Hover/focus states are driven with CDP CSS.forcePseudoState instead of
  moving the real cursor - transparent full-viewport overlay layers were
  swallowing every pointer hover, silently capturing zero hover states on
  hover-rich sites
- text-wrap (balance/pretty) captured and emitted (Tailwind text-balance/
  text-pretty, arbitrary fallback)
- Videos re-run <source> selection per viewport before frame-0
  normalization: resize-without-reload kept aspect-gated variants stuck on
  the load-time choice, poisoning mobile ground-truth screenshots
- svg roots capture their computed paint; fill="none" with a computed
  paint (the fill-current pattern) recovers the real color instead of
  rendering blank

Tokens & naming:
- Full CSS-Color-4 parsing (oklab/oklch/lab/lch/hsl) so modern colors
  cluster and earn semantic roles; visually-equal literals share one token;
  decoration/gradient/shadow colors consult the palette before minting
  opaque tokens (ridge: 45 opaque tokens -> 15, anthropic: 12 -> 7)
- Expanded role vocabulary (background/foreground/primary/accent/border/
  surface/muted) with deterministic tiebreaks and a chroma guard
- Section names mine CMS section ids and js-* hooks with hashy-suffix
  stripping (split_callout_JtTWTt -> split-callout-section)

364 tests pass (42 new), typecheck clean; determinism verified by
double-regen byte-comparison on two reference runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Samraaj Bath
2026-07-04 02:22:15 -07:00
co-authored by Claude Fable 5
parent db0054e43b
commit 6e4921884c
16 changed files with 968 additions and 54 deletions
+36
View File
@@ -784,3 +784,39 @@ describe("generateCss breakpoint-gated marquee transform band suppression (Defec
assert.ok(/matrix\(1, ?0, ?0, ?1, ?-40/.test(allRulesX(css, "n1")), `a non-animated per-band offset must be kept, got: ${allRulesX(css, "n1")}`);
});
});
// text-wrap emission: a heading authored `text-wrap:balance` must survive to the clone, or the
// title wraps differently. The initial `wrap` is elided (it's the default), so only authored
// balance/pretty/nowrap ship.
describe("generateCss text-wrap", () => {
it("emits an authored text-wrap:balance on a heading", () => {
const h1 = node("n1", "h1", computed({ textWrap: "balance" }));
const root = node("n0", "body", computed(), [h1]);
const css = generateCss(irWith(root), new Map());
assert.ok(/text-wrap:balance/.test(baseRule(css, "n1")), `text-wrap:balance emitted (got: ${baseRule(css, "n1")})`);
});
it("emits text-wrap:pretty", () => {
const p = node("n1", "p", computed({ textWrap: "pretty" }));
const root = node("n0", "body", computed(), [p]);
const css = generateCss(irWith(root), new Map());
assert.ok(/text-wrap:pretty/.test(baseRule(css, "n1")), `text-wrap:pretty emitted (got: ${baseRule(css, "n1")})`);
});
it("elides the default text-wrap:wrap (no noise)", () => {
const h1 = node("n1", "h1", computed({ textWrap: "wrap" }));
const root = node("n0", "body", computed(), [h1]);
const css = generateCss(irWith(root), new Map());
assert.ok(!/text-wrap/.test(baseRule(css, "n1")), `default text-wrap:wrap must not be emitted (got: ${baseRule(css, "n1")})`);
});
it("skips text-wrap on a child when it equals the parent (inherited)", () => {
// text-wrap is inherited: a child matching the parent's balance relies on inheritance.
const child = node("n2", "span", computed({ textWrap: "balance" }));
const h1 = node("n1", "h1", computed({ textWrap: "balance" }), [child]);
const root = node("n0", "body", computed(), [h1]);
const css = generateCss(irWith(root), new Map());
assert.ok(/text-wrap:balance/.test(baseRule(css, "n1")), "parent heading emits balance");
assert.ok(!/text-wrap/.test(baseRule(css, "n2")), `inherited child does not re-emit (got: ${baseRule(css, "n2")})`);
});
});