Vanilla JavaScript Projects

Project 52, Website sections

Sticky Header and Scroll Progress

A header that gets out of the way while you read and comes back the moment you scroll up, with a progress bar and a way back to the top.

Main API
scroll + requestAnimationFrame
Extra
IntersectionObserver
Dependencies
None
Your browser
Checking

Scroll the article

Scroll inside the box. Watch the header shrink, hide, come back and the bar fill up. Change the behaviour with the switches.

The Field Notes

A week walking the coast path

We started at the harbour before sunrise, with coffee in a flask and far too many snacks. The path climbs straight away, and the first hour is the hardest.

Cliffs and sea along a coastal path

By the second day the legs stop complaining. You learn to read the weather from the colour of the sea and to trust the little acorn signs at every gate.

What we packed

Two layers, a light rain jacket, a paper map and a phone with the route saved offline. Everything else was optional, and most of it stayed in the bag.

The best purchase was a pair of cheap walking poles. They saved our knees on every steep descent into the villages.

Where we stayed

Small guesthouses, one campsite and a pub with three rooms above the bar. Booking two weeks ahead was enough in September.

Small harbour village with boats

Breakfast was always huge, which meant lunch was usually a pasty on a bench looking at the sea.

Would we do it again?

Yes, but slower. Next time we will plan one rest day in the middle and stay two nights in the prettiest village.

If you are thinking about it, start with a short three-day section. By the end you will know if you want more.

Thanks for reading. The full route and our packing list are below.

How it works

  1. Read scroll once per frameThe scroll handler only asks for a frame. The real work runs in requestAnimationFrame, so scrolling stays smooth.
  2. Compare with last positionIf the new position is lower than the last one, the reader is going down, so the header hides.
  3. Progress is a fractionscrollTop divided by the scrollable height gives 0 to 1, which becomes the bar width.
let last = 0, ticking = false;
window.addEventListener("scroll", () => {
  if (ticking) return; ticking = true;
  requestAnimationFrame(() => {
    const y = scrollY, max = document.documentElement.scrollHeight - innerHeight;
    header.classList.toggle("small", y > 40);
    header.classList.toggle("hide", y > 160 && y > last);
    bar.style.width = (y / max) * 100 + "%";
    last = y; ticking = false;
  });
}, { passive: true });