Scroll-driven vs scroll-triggered animations on real sites

The advice you see everywhere says scroll-driven animations have arrived in CSS, so drop GSAP or AOS and stop shipping a JavaScript library for scroll effects. I agree with the direction. The problem is that the advice treats four different features as one thing, and they don’t ship together, fail the same way, or fit the same jobs. Geoff Graham separated them on CSS-Tricks, and his definitions are the reference for what follows. What I want to add is the part that decides the work on a WordPress site: which browsers ship which feature, and what the page looks like when they don’t.

My rule after using all of this on client builds: pick the feature by its fallback, not by its demo. If the effect is decorative and the worst case is that content sits there already visible, CSS is safe today. If the effect changes what a component does rather than how it looks, keep the JavaScript until the browsers catch up.

Scroll-driven animations and their three neighbors

A scroll-driven animation is tied to scroll progress. Scroll down and it advances, scroll up and it reverses, stop and it freezes mid-way. Reading-progress bars and hero parallax are the classic cases, and it’s just a normal @keyframes rule pointed at animation-timeline: scroll(). I’ve written before about replacing a JS library with exactly this, so I won’t rehash the basics.

A scroll-triggered animation starts when an element crosses a range and then runs to the end no matter what your thumb does next. That’s the reveal-on-scroll pattern clients ask for by name. In CSS you build it on the same animation machinery, but with a view() timeline and an animation-range that narrows when it starts. This is why people say scroll-driven and mean scroll-triggered about half the time, because it’s one module pointed at a different timeline.

Container query scroll-state doesn’t animate anything. Give an element container-type: scroll-state, then ask @container scroll-state(stuck: top), and the browser answers whether a sticky header is genuinely stuck right now. That’s the job we’ve done for years with a scroll listener and a class toggle, and the CSS version doesn’t run a handler on every scroll frame.

View transitions are the odd one out. They have nothing to do with scrolling, as the source article says plainly: same-document transitions animate a state change on one page, and cross-document transitions fade between pages. They’re worth having on a store and irrelevant to scroll. But the name sits right next to view(), and that collision alone justifies Graham’s summary table at the end of his post.

Browser support decides which one you ship

As of writing, caniuse puts animation-timeline with scroll() and view() in Chrome and Edge 115 and up, Firefox 159 and up, and Safari 26 and up. That’s close to everywhere. If your store’s analytics show current browsers, you can build on them and older visitors get nothing worse than a static page.

scroll-state queries are behind. Chrome and Edge shipped the stuck, snapped and scrollable states in Chrome 133, per the Chrome team’s post on scroll-state(), and Safari and Firefox still haven’t shipped them, so check caniuse before you promise a header redesign (how much this matters depends on how many of your customers sit on older iOS Safari, which is usually more than people expect). I covered the syntax in more detail earlier, including the trap that catches everyone on the first try: the scroll-state container can’t style itself, the rules have to target its descendants.

That gap is why my sticky headers still carry a small scroll listener, or I just accept the unshrunk look on Safari, while I do reveals in CSS without hesitating. A reveal that never fires leaves the content sitting there and nobody complains; a header that never compacts is a bug report.

Whatever you pick, wrap the movement in @media (prefers-reduced-motion: no-preference). It’s one line, and it keeps motion off for the people who asked for it.

A reveal with no JavaScript

This is the whole component, a group block with one extra class:

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .wp-block-group.reveal > * {
      animation: bbioon-reveal both;
      animation-timeline: view();
      animation-range: entry 10% cover 35%;
    }
  }
}

@keyframes bbioon-reveal {
  from { opacity: 0; translate: 0 2rem; }
  to   { opacity: 1; translate: 0; }
}

To use it, select the group in the block editor, open the Advanced panel in the sidebar, and add reveal to the additional CSS classes. Every direct child gets its own view() timeline, so a stack of paragraphs or product cards reveals one by one as each enters the viewport.

The @supports check is the load-bearing part. Where animation-timeline is missing, no animation applies, so nothing can start hidden and stay hidden. That was always the worst thing about the old reveal plugins: content sat at opacity: 0 until their JavaScript ran, and on a slow phone connection that meant blank sections and a rough Largest Contentful Paint.

One gotcha that MDN documents and I relearn every few months: the animation shorthand resets animation-timeline to auto, so the timeline declaration has to come after the shorthand, never before. If your animation runs once on load and ignores scrolling, that’s almost always it.

Next time you have a theme open, run grep -rn “addEventListener” wp-content/themes/your-theme/ and read what the scroll listeners do. Anything tying style to scroll position is a candidate for animation-timeline, and anything toggling a stuck class is a candidate for scroll-state once support arrives. If you want a hand swapping a theme’s scroll JavaScript for CSS with a fallback that never hides content, that’s work I take on, and it usually starts with that grep.

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.

Leave a Comment