- Off-viewport in-flow items (carousel slides beyond the right edge) keep
their per-viewport geometry deltas - they occupy layout and set track
cross-sizes even when not visible
- Height chains: single-viewport probe verdicts accepted for nodes that
only paint at one band; un-probed picture/object-fit:cover children
filling the parent count as height-deriving; a node whose own probe says
authored-explicit keeps its height at emission (twin of the flow guard)
- Symmetric authored-width circular guard in the walker (height-only
before), harvesting shadow-root adopted/style sheets for custom elements
- Auto margins only when width is genuinely content-constrained; gutter-
inset full-width blocks keep literal margins
- grid-template-rows: subgrid never dropped; variant-only subgrid intent
re-enters via partial-coverage escape; grid-item percentage widths bail
to auto (they resolve against the track, not the container)
- Banded raw-prop detection counts non-raw band overrides so a banded
bg-none reset isn't beaten by an inlined gradient
399 tests pass (13 new), typecheck clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Walker serializes the composed (flattened) tree: open shadow roots walk
as host children with <slot> replaced by its assigned light nodes (or
fallback), so custom-element content (product cards, web components) is
captured instead of arriving childless
- In-flow visibility:hidden nodes with a nonzero border box survive the
prune as sized invisible placeholders - ghost spacers carry load-bearing
geometry
- When pruning drops leading in-flow children of a horizontally-translated
track, the baked translateX is re-anchored by their aggregate margin-box
width (settled carousel tracks return to origin instead of pushing real
content off-screen); animation-owned transforms unaffected
- Circular-shrink slide guard counts block-level fill children as
width-deriving regardless of wAuto (block auto-width IS fill), pinning
library-sized slide widths uniformly
- javascript: hrefs emit as "#" and the link gate normalizes both sides
382 tests pass (18 new), typecheck clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
Capture:
- Preserve real asset extensions (.lottie no longer truncated); extract the
default animation JSON from dotLottie ZIP containers at materialization
(minimal built-in ZIP reader, no new deps)
- Reset scroll + settle before every viewport snapshot so scroll-linked
styles can't bake into captured computed values
- Marquee detection now requires content overflow (>1.35x), sustained
velocity across separated windows, scroll independence (jiggle test), and
genuine content repetition — kills scroll-settle-lerp false positives
- Sizing probe trusts authored explicit heights (px/vh/calc) over circular
parent/child reflow verdicts, fixing dropped 100vh heroes
Normalize/generate:
- Canonicalize identity transforms to "none" per viewport; emit explicit
transform resets across bands when any band has a real transform
- DittoLottie keeps the captured placeholder until DOMLoaded; failed loads
no longer blank the container
- mx-auto only for true auto-margin centering; literal per-band margins are
preserved (fixes full-bleed regressions)
- Spacing-scale snap tightened to 0.25px (3.5px stays p-[3.5px]); emit
whitespace-nowrap for wrap-vulnerable single-line text leaves
- Grid recipes no longer override captured track counts or column spans;
responsive re-flow plans apply only when geometry agrees
Quality scoring:
- Rewritten 6-dimension rubric (payload, decomposition, duplication,
semantics, hygiene, runtime) with catastrophe caps and per-dimension
reporting; thresholds are documented calibration guides
218 tests pass (74 new), typecheck clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- DittoWire/Accordion/DropdownMenu runtime templates: AbortController-based
listener cleanup, idempotent effects, orphan panel removal on unmount
- Emit a single src/lib/utils.ts cn() instead of a copy per component file
- Collapse captured whitespace runs under white-space:normal (pre* preserved)
- Tag source-injected font-metric probe nodes at capture; drop them from IR
- Snap sub-pixel arbitrary lengths (integer within 0.1px, else 1 decimal)
- Emit SITE_ORIGIN (src/lib/site.ts, env-overridable): sitemap/robots/
canonical/og:url/JSON-LD resolve against the clone's own origin instead of
the source domain
- Pin lottie-web in generated package.json whenever DittoLottie is emitted
144 tests pass (21 new), typecheck clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Round-3 fixes from gate validation + human visual review of fresh
cropin.com and ooni.com clones:
- app.ts: section data-var rename used an unanchored substring replace,
so Tile2_data matched inside MediaTile2_data and emitted a corrupted
Mediatile2Data usage (ReferenceError at prerender). Word-boundary
regex; every map site now shares one derivation.
- graft.ts: about:blank iframes were blanket-skipped, missing the
Klaviyo newsletter form (mounted into a blank same-origin frame via
JS) — the audit's #1 complaint. Blank frames now graft; the
visibility gate still excludes 0-size tracking pixels.
- walker.ts: isVisible() now rejects boxes wholly outside the viewport
(off-left always; off-right only when the page isn't horizontally
scrollable; fixed boxes fully above/below), so a closed slide-in
drawer's contents no longer count as expected text (91.8% -> 93.4%).
- css.ts: four sizing-regime corrections — pin captured px for
circular shrink-0 slides (Splide slide chain collapsed 0x0);
flex-basis:100% for full-width shrink-0 slides; keep fixed-px grid
templates for scrolling track lists (repeat(N,1fr) squished a
50-track carousel); single full-bleed fixed track -> minmax(0,1fr);
keep authored heights whose in-flow children are fill children
(aspect-video heroes inflated 240 -> 720px); width:100% for
inset-spanned aspect boxes.
Gate scores: ooni home 88.7 -> 93.3 (responsive 32 fails -> pass),
pizza-ovens 90.8 -> 97.6 (perceptual 46.7% -> 17.2%).
107/107 compiler tests, all workspaces green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>