Scroll animation without a library

Sticky positioning, one shared rAF loop, and a CSS custom property will carry a surprising amount of choreography.

Sample post. This entry is placeholder content used to build out the journal. It is not a statement by or about Tecnicubes.

Reaching for an animation library is reasonable. It is also worth knowing what the platform gives away for free, because the answer has changed a lot.

Let the browser do the pinning

position: sticky pins an element without JavaScript and without forcing layout on every frame. A tall track with a sticky child is the whole mechanism behind most pinned-scene effects.

One loop, not one per scene

The cost is rarely the animation itself; it is having twenty listeners each doing their own measurement. Register scenes into one set, run one requestAnimationFrame loop, and skip anything off screen.

function tick() {
  frame = 0;
  const y = window.scrollY;
  for (const scene of active) {
    const p = clamp01((y - scene.top) / scene.travel);
    scene.el.style.setProperty('--p', p.toFixed(4));
  }
}

Publish progress, don’t re-render

Writing a custom property lets CSS do the interpolation on the compositor. The component tree never re-renders while scrolling, which is usually the difference between smooth and not.

If your scroll handler calls setState, you have already lost the frame budget.

Respect the setting

prefers-reduced-motion is not a nice-to-have. Collapse the tracks, resolve every scene to its composed state, and let the page become an ordinary stack of panels.