Scroll spy without IntersectionObserver
Highlighting navigation links based on scroll position used to require JavaScript IntersectionObserver or scroll event listeners. Now CSS can track which section is in view with scroll-target-group and the :target-current pseudo-class.
2const observer = new IntersectionObserver(
3 (entries) => {
4 entries.forEach(entry => {
5 const link = document
6 .querySelector(`a[href="#
7 ${entry.target.id}"]`);
8 link.classList.toggle(
9 'active',
10 entry.isIntersecting);
11 });
12 },
13 { threshold: 0.5 }
14);
15
16document.querySelectorAll('section')
17 .forEach(s =>
18 observer.observe(s));
2 overflow-y: auto;
3}
4
5nav a:target-current {
6 color: var(--accent);
7}
Zero JavaScript
No IntersectionObserver, no scroll event listeners, no class toggling. The browser tracks which section is in view natively.
Always in sync
The :target-current pseudo-class updates in real-time as the user scrolls. No timing bugs or threshold tuning.
Works with CSS scroll-snap
Pairs naturally with scroll-snap for section-based layouts. The active indicator follows snap points automatically.
How it works
Scroll spy navigation — highlighting the current section's link as the user scrolls — has always required JavaScript. The typical approach uses IntersectionObserver to watch each section, then toggles an 'active' class on the corresponding nav link. This works but requires careful threshold tuning, cleanup on unmount, and can fall out of sync with fast scrolling.
The CSS approach uses :target-current, a pseudo-class that matches anchor links pointing to the element currently in the scroll port. Combined with smooth scrolling and scroll-snap, you get a complete scroll-spy navigation with no JavaScript at all. The browser handles the tracking natively, so it's always perfectly in sync.