Sticky Section Nav
A table of contents whose marker travels to whichever section is actually being read.
Overview
What acme says about overview.
Pricing
What acme says about pricing.
Security
What acme says about security.
Support
What acme says about support.
Build a table of contents that tracks the section being read. Structure: a narrow nav of headings beside a scrolling content column. The component owns its OWN scroll container rather than driving off the window, so it works anywhere on a page. The rule that makes it feel right: A SECTION BECOMES ACTIVE WHEN IT CROSSES A LINE ABOUT A THIRD OF THE WAY DOWN, not when it touches the top edge. Compute it as scrollTop + clientHeight/3 and divide by the section height. Using the top edge flips the highlight a beat before the heading is even readable, and the list always feels one step ahead of the reader. Use ONE shared marker (framer-motion layoutId, or a View Transition name) that slides down the rail, on a spring at stiffness 520, damping 40. Four markers that light up carry no direction; the travel is what says which way you moved. Performance: coalesce scroll with ONE requestAnimationFrame per burst, ignoring further events while a frame is pending, and cancel it on unmount. It must STEER as well as read: clicking a heading scrolls to that section. A contents list that only reports position is doing half the job. Keep inactive headings in muted ink, still legible. Hiding them turns a contents list into a single label. Accessibility: nav with an aria-label, aria-current on the active heading, and a label on the scrollable region. Under prefers-reduced-motion, drop the shared marker and use instant scrolling on the jump-to. Both behaviours are intact. Colors as tokens: accent for the marker, primary ink for the active heading, muted for the rest, hairline for the section rules. Build the component, not this demo of it. - Ambient motion that IS the component may loop: a drifting background, a marquee, a spinner while something is genuinely loading. - Do not add any timer, interval, loop or script that performs a person's action by itself. No auto-hover, no auto-click, no cycling through states, no pretend submitting, no replaying the interaction on a schedule. - Every state that answers a person waits for a real event from that person. - Placeholder copy and sample content are welcome. Simulated behaviour is not.