Scroll animations with CSS sibling-index(), not JavaScript

The standard advice for high-end scrollytelling has been “just use GSAP or a scroll listener.” Those tools are powerful, and they are also tied to the main thread. I have watched plenty of projects, including expensive ones built for big agencies, choke on mobile because someone animated hundreds of characters out of a scroll event. While the main thread is busy working out positions, frames drop and the immersive experience turns into a stuttering one.

JavaScript is usually the bottleneck once animations scale up. CSS sibling-index() and the scroll-timeline API move that work to the compositor instead. You can rebuild something like a “text vortex,” where every single character animates on its own, without the performance cost the JavaScript version brings with it.

What CSS sibling-index() actually does

Staggering an animation by an element’s position used to mean looping through the DOM in JS or hardcoding nth-child rules in your stylesheet, and nobody enjoys maintaining either one. That is a big part of why devs reached for JS libraries: index-based styling was easy there. Native CSS sibling-index() hands the calculation to the browser, which is where it belongs.

If you’ve been following my previous notes on better CSS bar charts, you know I’m a pragmatist. I don’t use new features just because they are shiny; I use them because they solve the “mobile jank” problem that haunts modern scrollytelling.

We still need a little script

To animate characters, we first have to tokenize the string. While I’d love to say CSS can handle this, we still need a tiny bit of JS to split the text into divs. I prefer using the GSAP SplitText plugin because it handles accessibility (aria-labels) out of the box, even if we aren’t using the actual GSAP animation engine.

const el = document.querySelector(".vortex");
el.innerHTML = el.innerHTML.replaceAll(/\s/g, '⠀');
new SplitText(".title", { type: "chars", charsClass: "char" });

I refactored this recently for a client with a broken checkout page. The culprit turned out to be a global scroll listener attached to their “pretty” footer. Moving to the hybrid approach fixed the footer without breaking the rest of the UI. JS for the markup, CSS for the motion.

Building the vortex in CSS

Once your characters are wrapped in .char divs, CSS sibling-index() and sibling-count() take over. A little math on each character’s index against the total count gives you its radius and rotation.

.vortex {
  position: fixed;
  animation-timeline: scroll();

  .char {
    /* The Magic Sauce */
    --radius: calc(10vh - (7vh / sibling-count() * sibling-index()));
    --rotation: calc((360deg * 3 / sibling-count()) * sibling-index());

    transform: rotate(var(--rotation)) 
               translateY(calc(-2.9 * var(--radius))) 
               scale(calc(.4 - (.25 / sibling-count() * sibling-index())));
    
    animation-name: fade-in;
    animation-range-start: calc(90% / sibling-count() * sibling-index());
    animation-timeline: scroll();
  }
}

This is how you kill framework bloat. Tie the animation to the scroll-timeline and the browser only updates the visuals when the scroll position changes, off the main thread.

If the scroll animation work is eating your dev hours, hand it to me. I have been wrestling with WordPress since the 4.x days.

Ahmad’s takeaway

The “it is too complex for CSS” excuse is running out of road. If you are still building scroll animations on window.addEventListener('scroll'), you are leaving a mess for whoever maintains the site next. Try these native functions on something small first. Even with a video fallback for Firefox, the performance you get on Chromium-based browsers, and eventually on all of them, pays for the refactor.

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.