Skip to main content
motion

Scroll Animation Is Usually a Content Problem

Scroll-triggered animation often hides weak content. Before adding another reveal animation, ask whether the content actually earns it.


5 min read

Scroll-triggered animation has a specific legitimate use: revealing information progressively, establishing spatial relationships between elements, or guiding attention through a designed sequence. It also has a much more common illegitimate use: making a weak page feel like something is happening when the user scrolls, so they don't immediately notice that the content isn't worth reading.

When you see a marketing page where every section fades in as you scroll, ask whether the animations are serving the user or masking a copywriting problem.


When Scroll Animation Is Justified

Revealing information progressively. A step-by-step process diagram where each step appears as you scroll through the explanation. The animation is synchronized to the text — the visual appears when the text that explains it is in view. The user reads and sees simultaneously. This is scroll animation earning its place.

Establishing spatial relationships. A product tour that shows a UI element in context, then zooms or translates to highlight a specific feature as the user scrolls. The motion communicates hierarchy and spatial relationships that static layout can't. This is scroll animation as information architecture.

Data visualization builds. A chart that constructs itself as you scroll into its section. The animation makes the data's structure visible in a way a static chart doesn't — you see the trend build rather than just the final state. The motion is the communication.

These share a common trait: remove the animation, and you lose information. The scroll-triggered motion is doing work that has no static equivalent.

When Scroll Animation Is a Symptom

Every section fades in. If every <section> on the page has a fade-and-rise entrance triggered by Intersection Observer, the animation is not communicating anything section-specific. It's a blanket treatment that says "this page uses scroll animation" rather than "this specific content reveals itself this specific way because of this specific reason." The content could say anything. The animation would be the same.

Content that should be visible immediately is hidden until scroll. Above-the-fold content that fades in after a 600ms delay, or sections that start with opacity: 0; transform: translateY(40px) and reveal on scroll — this pattern makes users work to see content they would otherwise see immediately. You're adding friction to reading. The implicit message is that the page is so well-designed that even the act of revealing it is an experience. This is designer ego.

The page has nothing interesting on first view. A landing page where the first screen is a full-viewport video with a headline, followed by five scrollable sections of animated feature callouts — if you screenshots the first viewport, is there enough there to make a user want to scroll? Scroll animation cannot fix a first impression problem. It can only delay the moment when the user decides the page isn't for them.

The Performance Cost

Scroll-triggered animation using Intersection Observer is relatively cheap. Scroll-triggered animation using scroll event listeners with un-throttled handlers is not. The pattern that kills performance:

// Don't do this
window.addEventListener("scroll", () => {
  elements.forEach((el) => {
    const rect = el.getBoundingClientRect();
    if (rect.top < window.innerHeight) {
      el.classList.add("visible");
    }
  });
});

Every scroll event — firing at 60fps — triggers layout queries (getBoundingClientRect) and DOM writes. On mobile, this destroys frame rate during scroll, which is the opposite of what you wanted.

Use Intersection Observer instead:

const observer = new IntersectionObserver(
  (entries) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        entry.target.classList.add("visible");
        observer.unobserve(entry.target); // fire once, stop watching
      }
    });
  },
  { threshold: 0.15 }
);

document.querySelectorAll("[data-animate]").forEach((el) => observer.observe(el));

Even better: use CSS to handle the transition and JavaScript only to set the trigger class. Keep motion logic in CSS, state changes in JS.

The Audit

If you have scroll animation on a page, run this audit:

  1. Disable all scroll-triggered animation (set prefers-reduced-motion: reduce in DevTools or add a temporary CSS override). Read the page. Is it still compelling? Does it communicate the same things? If yes, the animations were decoration.

  2. Screenshot each section as it appears at scroll trigger. Does it look like a coherent piece of content, or does it look like an empty section waiting to animate? If the initial state — opacity: 0; transform: translateY(20px) — reveals that the section has no visual structure before the animation, the animation is papering over a layout problem.

  3. Ask a user to scroll the page while you watch. Do their eyes follow the animated elements? Or do they glance at the animation and keep reading? If the animation draws attention without directing it anywhere useful, it's noise.

Scroll animation that survives all three of these checks is earning its place. Most doesn't. Most is there because it was easy to add, looked impressive in the prototype review, and no one had a principled reason to remove it.

Ship the animation that has a reason. Cut the animation that has a feeling.