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>
50 lines
1.9 KiB
HTML
50 lines
1.9 KiB
HTML
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Reveal fixture</title>
|
|
<style>
|
|
body { margin: 0; font-family: sans-serif; }
|
|
.spacer { height: 2200px; background: linear-gradient(#eee, #ddd); }
|
|
/* Elementor pattern: content wrappers hidden at load, revealed by a waypoint class
|
|
swap that applies an entrance keyframe animation. */
|
|
.elementor-invisible { visibility: hidden; }
|
|
@keyframes fadeInUp {
|
|
from { opacity: 0; transform: translate3d(0, 40px, 0); }
|
|
to { opacity: 1; transform: none; }
|
|
}
|
|
.animated { animation-duration: 0.4s; animation-fill-mode: both; }
|
|
.fadeInUp { animation-name: fadeInUp; }
|
|
.reveal-box { height: 220px; background: #2e8b57; color: #fff; padding: 20px; }
|
|
#never { visibility: hidden; height: 60px; background: #999; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Reveal fixture</h1>
|
|
<div class="spacer"></div>
|
|
<!-- Waypoint reveal: hidden at load, class-swapped when scrolled into view. -->
|
|
<div id="reveal-io" class="reveal-box elementor-invisible" data-io-reveal data-settings='{"_animation":"fadeInUp"}'>
|
|
<p>Scroll-revealed content (IntersectionObserver)</p>
|
|
</div>
|
|
<div class="spacer"></div>
|
|
<!-- Same pre-reveal marker but keyed to a trigger the dwell walk cannot fire:
|
|
only the known-library neutralization reveals it. -->
|
|
<div id="reveal-far" class="reveal-box elementor-invisible" data-settings='{"_animation":"fadeInUp"}'>
|
|
<p>Reveal keyed to a non-scroll trigger</p>
|
|
</div>
|
|
<!-- Genuinely hidden, non-library content: must STAY hidden in the capture. -->
|
|
<div id="never">permanently hidden</div>
|
|
<script>
|
|
const io = new IntersectionObserver((entries) => {
|
|
for (const e of entries) {
|
|
if (!e.isIntersecting) continue;
|
|
e.target.classList.remove("elementor-invisible");
|
|
e.target.classList.add("animated", "fadeInUp");
|
|
io.unobserve(e.target);
|
|
}
|
|
}, { threshold: 0.1 });
|
|
for (const el of document.querySelectorAll("[data-io-reveal]")) io.observe(el);
|
|
</script>
|
|
</body>
|
|
</html>
|