Section one
Each block fades in and lifts as its top edge crosses into view.
Elements fade in and rise as they enter the viewport — driven by CSS alone.
Scroll Reveal
The most requested effect on any landing page, and the one AI builders most often get wrong — they reach for a scroll listener when the browser can do it on the compositor thread for free.
Each block fades in and lifts as its top edge crosses into view.
The animation is bound to this panel's scrollport, not to a timer.
Scroll back up and it reverses automatically. There is no state to reset.
That reversibility is what separates scroll-driven animation from a one-shot reveal.
Reduced motion is on. Your system setting disables this effect by design — turn off “Reduce motion” to see the demo.
Add a scroll reveal effect to the page. Each content block should start with opacity 0 and translateY(28px), and animate to opacity 1 and translateY(0) as it enters the viewport. Implementation requirements: - Use CSS scroll-driven animations: animation-timeline: view() with animation-range: entry 10% cover 35%. Do not use a scroll event listener. - Include animation-fill-mode: both so elements are visible before and after the range. - Add a @supports fallback for browsers without animation-timeline using IntersectionObserver, threshold 0.25. - Animate only opacity and transform — never top, margin or height. - Respect prefers-reduced-motion: when reduced motion is requested, show all content immediately with no animation. - Stagger consecutive blocks by 60ms using animation-delay. Do not apply this to content above the fold; the first screen must be readable without waiting.
animation-timeline: view() binds the animation to the element's own position relative to the scrollport. It runs off the main thread, so it keeps animating while JavaScript is busy.animation-range: entry 10% cover 35% is the part most AI output omits. entry 10% means "start when the element's leading edge has crossed 10% into the viewport"; cover 35% means "finish by the time it is 35% covered". Without a range the animation maps to the entire scroll journey and feels sluggish.animation-fill-mode: both keeps the element at the start keyframe before entry and at the end keyframe after exit — without it you get a flash of fully visible content, then a jump.view() works without the whole page scrolling.| Browser | Support |
|---|---|
| Chrome / Edge | 115+ — full support, runs on the compositor |
| Safari | 26+ — supported; earlier versions fall back |
| Firefox | Behind a flag — the fallback path is what users get |
Because the fallback exists, this is safe to ship today. The cost of the CSS path is close to zero: no scroll listener, no layout reads, no jank when the main thread is busy. The IntersectionObserver fallback does cost a callback per threshold crossing, which is why the threshold is coarse (0.25) rather than continuous.
Don't animate the first viewport. A visitor who has to wait 400ms to read your headline has already decided the page is slow. Above the fold should be static.
Don't reveal everything. If every paragraph fades in, the page feels like a slideshow and the effect stops signalling anything. Reserve it for distinct sections, two to five per page.
Don't hide content that carries SEO weight. Text that starts at opacity: 0 is still in the DOM and still indexed, but a user who lands mid-page from a search result may see blank space. Anchor jumps and back-navigation should bypass the animation.