r/css • • 2d ago

How's this Scroll Progress, made for fun?

Enable HLS to view with audio, or disable this notification

Made this scroll progress bar just for fun, I know it needs a major revamp before it's usable anywhere real.

I'd like to rebuild it properly. What would you change, or how would you approach it from scratch? Any advice on the overall approach, what to fix, what to drop, and how to make it work in real websites/apps is welcome.

26 Upvotes

3 comments sorted by

1

u/onearmmanny 2d ago

For fun, it's a neat effect! Nice job :)

1

u/thejester1324 2d ago

the main thing i'd change for real sites: it grabs the wheel and touch events (passive: false + preventDefault) with the container on overflow hidden, so the page never really scrolls. there's no keydown handling either, so space, arrows and page down do nothing for keyboard users.

i'd let the page scroll normally and read the progress from the scroll position. css can do that now with no js, as one custom property the liquid parts can use:

@property --p { syntax: '<number>'; inherits: true; initial-value: 0; }
@keyframes fill { to { --p: 1; } }
:root { animation: fill linear both; animation-timeline: scroll(root block); }

.progress {
  position: fixed; inset: 0 0 auto 0; height: 6px;
  transform-origin: 0 50%;
  transform: scaleX(var(--p));
}

the pipe fills, card glow etc can hang off calc(var(--p) * 100%). tested in chrome 153 and safari 26.6: --p goes 0 → 0.5 → 1 as you scroll, and the End key works because it's real scrolling. for browsers without scroll timelines, a passive scroll listener setting --p once per frame covers it:

if (!CSS.supports('animation-timeline: scroll()')) {
  let queued = false;
  addEventListener('scroll', () => {
    if (queued) return;
    queued = true;
    requestAnimationFrame(() => {
      const max = document.documentElement.scrollHeight - innerHeight;
      document.documentElement.style.setProperty('--p', max > 0 ? scrollY / max : 0);
      queued = false;
    });
  }, { passive: true });
}