Wave 4: reveal settling + replay, 206/magic-byte media fix, hidden-geometry banding, bare-zero utility gating
Capture (4a): - stabilize: dwell-scroll settling pass + known-library pre-reveal neutralization after lazy promotion, so every viewport snapshot records the post-reveal steady state (about-page sections no longer captured blanket-hidden: 24 base `invisible` wrappers -> 0) - probeReveals: extended to the visibility + entrance-animation family; DittoMotion replays them (JS re-hide on mount, IntersectionObserver reveal, 4s force-reveal failsafe) so SSR/non-JS still shows content - assets: 206 range-response bodies are no longer stored as full assets (full-fetch fallback) + video magic-byte validation - the about-page hero video is now a valid 6.0MB ftyp mp4 instead of 18.9KB of garbage Generate (4b): - css: hidden-node geometry policy - a visibility:hidden box with a 0x0 captured bbox at a band becomes display:none there; an OCCUPYING hidden box gets the captured per-viewport geometry instead of the baked canonical one. Also covers the ancestor-hidden-at-base case (the cropin.com/cotton slider arrow: hide inherited from an elementor-widget ancestor at every width parked a desktop left:548px box inside a 375px viewport -> body.scrollWidth 585; now 375 on both verified pages) - tailwind: bare-zero utility gating - font-size:0 -> text-[0px] and letter-spacing:0 -> tracking-[0px] (text-0/tracking-0 are silently invalid in Tailwind v4; fixes permanently-visible map labels on the cotton Global-presence section) Verified: 87/87 compiler tests green, full-workspace suite green, regenerated cropin.com + /cotton/ builds measure 375px scrollWidth at a 375px viewport, and a fresh /about/ clone renders all reveal sections with scroll-replay against the live site. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
9aed2540aa
commit
199359d0a6
@@ -3012,24 +3012,41 @@ export function collectNodeRules(ir: IR, assetMap: Map<string, string>, includeN
|
||||
// (declsForViewport emits `visibility:hidden`; parent-equality keeps it own-only).
|
||||
} else if (ownNone || !node.visibleByVp[b.vp]) {
|
||||
const shownAtBase = node.visibleByVp[baseVp] && (node.computedByVp[baseVp]?.display || "") !== "none";
|
||||
if (ownNone) {
|
||||
// Own display:none removes the box from layout entirely. Emit the hide even when the node
|
||||
// is ALSO hidden at base — a visibility:hidden base still bakes an OCCUPYING box (see
|
||||
// above), so without this band the canonical geometry would render at this width. Skip
|
||||
// only when the base itself is display:none (the band would be redundant).
|
||||
if ((node.computedByVp[baseVp]?.display || "") !== "none") {
|
||||
// Hidden by an ANCESTOR's visibility here AND at base (the ancestor's own rule carries
|
||||
// the hide at every width) — but a visibility:hidden box still PARTICIPATES in layout,
|
||||
// and the base rule bakes CANONICAL geometry. Same policy as the own-hidden path above:
|
||||
// a 0x0 box gets display:none; an occupying box falls through to the per-viewport delta
|
||||
// so it sits where the capture measured it at THIS width — not parked at e.g. a desktop
|
||||
// left:548px inside a 375px viewport (the cropin.com/cotton slider arrow, +210px of
|
||||
// sideways scroll at 375 with the hide inherited from an elementor-widget ancestor).
|
||||
const ancestorHiddenHere = !ownNone && /^(hidden|collapse)$/.test(vpCs.visibility || "");
|
||||
if (ancestorHiddenHere && !shownAtBase) {
|
||||
const bb = node.bboxByVp[b.vp];
|
||||
if (!bb || bb.width <= 0 || bb.height <= 0) {
|
||||
nr.bands.push({ media: b.media, decls: new Map([["display", "none"]]) });
|
||||
continue;
|
||||
}
|
||||
} else if (shownAtBase) {
|
||||
// Hidden by an ancestor (or zero-size / opacity:0): geometry overrides are breakpoint
|
||||
// noise — the ancestor's own hide (or the reveal replay, for scroll-reveal opacity)
|
||||
// covers it. Emit only the hide the node itself carries.
|
||||
const hide = new Map<string, string>();
|
||||
if (pf(vpCs.opacity) === 0 && !animOwned.has("opacity")) hide.set("opacity", "0");
|
||||
if (/^(hidden|collapse)$/.test(vpCs.visibility || "")) hide.set("visibility", "hidden");
|
||||
if (hide.size) nr.bands.push({ media: b.media, decls: hide });
|
||||
// Occupying: fall through to the normal per-viewport delta below.
|
||||
} else {
|
||||
if (ownNone) {
|
||||
// Own display:none removes the box from layout entirely. Emit the hide even when the node
|
||||
// is ALSO hidden at base — a visibility:hidden base still bakes an OCCUPYING box (see
|
||||
// above), so without this band the canonical geometry would render at this width. Skip
|
||||
// only when the base itself is display:none (the band would be redundant).
|
||||
if ((node.computedByVp[baseVp]?.display || "") !== "none") {
|
||||
nr.bands.push({ media: b.media, decls: new Map([["display", "none"]]) });
|
||||
}
|
||||
} else if (shownAtBase) {
|
||||
// Hidden by an ancestor (or zero-size / opacity:0) but visible at base: geometry
|
||||
// overrides are breakpoint noise — the ancestor's own hide (or the reveal replay,
|
||||
// for scroll-reveal opacity) covers it. Emit only the hide the node itself carries.
|
||||
const hide = new Map<string, string>();
|
||||
if (pf(vpCs.opacity) === 0 && !animOwned.has("opacity")) hide.set("opacity", "0");
|
||||
if (/^(hidden|collapse)$/.test(vpCs.visibility || "")) hide.set("visibility", "hidden");
|
||||
if (hide.size) nr.bands.push({ media: b.media, decls: hide });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const centeredVp = stableCenter || (layoutParent ? centeredAtVp(node, layoutParent, b.vp) : false);
|
||||
const vpDecls = finalizeDecls(declsForViewport(node, parentNode?.computedByVp[b.vp], b.vp, assetMap, centeredVp, colorVar, ir.doc.perViewport[b.vp]?.scrollHeight, widthPlan, gridColsByVp?.get(b.vp), gridRowsByVp?.get(b.vp), flowH, dropInsets, leftPct, heightFill, geometry, dropGridRows, dropViewportMaxWidth), tokenResolver);
|
||||
|
||||
Reference in New Issue
Block a user