Section navigator
A floating section list that follows the active part of a scrollable document.
Overview
A focused introduction to the page.
Principles
The decisions guiding this interface.
Details
A closer look at the small interactions.
Next steps
Where to go after this section.
Implementation
1"use client";2 3import { useEffect, useState, type RefObject } from "react";4import { ChevronDown, List } from "lucide-react";5 6export type SectionNavigatorItem = { id: string; title: string };7export type SectionNavigatorProps = {8 sections: SectionNavigatorItem[];9 scrollContainer: RefObject<HTMLElement | null>;10};11 12/** A compact section picker that tracks headings inside a scroll container. */13export function SectionNavigator({14 sections,15 scrollContainer,16}: SectionNavigatorProps) {17 const [active, setActive] = useState(sections[0]?.id ?? "");18 const [open, setOpen] = useState(false);19 20 useEffect(() => {21 const root = scrollContainer.current;22 if (!root || !sections.length) return;23 const observer = new IntersectionObserver(24 (entries) => {25 const visible = entries26 .filter((entry) => entry.isIntersecting)27 .sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top);28 if (visible[0]) setActive(visible[0].target.id);