FADE DEMO

Section One

Scroll down slowly to see each card fade in via IntersectionObserver, triggered by a rootMargin that effectively fires at 88% of the viewport height. Scroll an element out of view and back in — it re-animates every time.

Card 1 — no delay
Card 2 — 120ms stagger
Card 3 — 240ms stagger

Section Two

Now scroll back up. Direction is inferred from entry.boundingClientRect.top vs entry.rootBounds.top — scrolling down applies translateY(-20px), scrolling up applies translateY(20px), before the "in" class triggers the transition.

Card 1 — no delay
Card 2 — 120ms stagger
Card 3 — 240ms stagger

Section Three

This build uses a single IntersectionObserver (threshold: 0, rootMargin: '0px 0px -12% 0px'), will-change: transform, opacity for GPU compositing, and respects prefers-reduced-motion by only fading opacity with no transform offset.

Card 1 — no delay
Card 2 — 120ms stagger
Card 3 — 240ms stagger
Scroll to test ↑↓