CSS Animation Example: Reveal on Scroll

The "Reveal on Scroll" effect introduces elements to the screen dynamically as the user scrolls, creating a feeling of narrative flow and polish.

Scroll down slowly 👇

I magically appear!

This box faded in and slid up when it entered the view.

I'm slightly delayed!

You can stagger animations easily using transition-delay.

Keep scrolling...

Here comes another!

The observer keeps watching and animating items down the page.

While experimental CSS features like animation-timeline: view() exist, the most robust and widely supported method for scroll animations is combining CSS classes with the JavaScript Intersection Observer API.

In CSS, we define an initial hidden state using opacity: 0 and transform: translateY(50px), alongside a transition. We then create an .is-visible class that sets opacity to 1 and transform to translateY(0). The Intersection Observer strictly watches the elements, and precisely when they cross the viewport threshold (e.g., 20% visible), it appends the .is-visible class, triggering the CSS transition.

Here is the combined CSS and JS code to create the Reveal on Scroll animation.

View Output

Accessibility Considerations

As elements sliding up the screen rapidly can cause discomfort for users with vestibular disorders, we must respect their preferences. prefers-reduced-motion is a CSS media feature used to detect if a user has enabled a setting on their device to minimize the amount of non-essential animation or motion. We can use it to provide specific styles for users who have enabled this setting.

By removing the transition duration and the initial transform displacement, the elements will simply appear instantly when they scroll into view, rather than animating their arrival.

Here's what we used in our example:


@media (prefers-reduced-motion: reduce) {
    .reveal-card {
        /* Kill the sliding transition entirely */
        transition: none; 
        
        /* Remove the initial offset so it doesn't jump */
        transform: none; 
    }
}