Xzero UI
Pro library

Table of contents progressPro

A table of contents that reads along with you: a line bends through the nested headings, fills as you scroll, and carries a marker to the current section, with smooth jumps and a time-left countdown.

$ pnpm dlx shadcn@4.23.0 add @xzero-ui-pro/toc-progress
Live · keyboard ready

When to use

When not to use

Installation

Register both Xzero UI namespaces in your project's components.json:

{
  "registries": {
    "@xzero-ui": "https://mcp.xzero.co.uk/r/{name}.json",
    "@xzero-ui-pro": "https://mcp.xzero.co.uk/r/pro/{name}.json"
  }
}
npx shadcn@4.23.0 add @xzero-ui-pro/toc-progress

Install xzero-foundation once, and import @/components/xzero/foundation.css in your app root. No Xzero token is used by this snapshot. For sample portraits, photos, fonts, or logos, download https://mcp.xzero.co.uk/install-assets.mjs and run node xzero-install-assets.mjs <receiving-project>.

Usage

import { TocProgress } from "@/components/xzero/toc-progress/toc-progress";

const headings = [
  { id: "overview", label: "Overview" },
  { id: "install", label: "Installation" },
  { id: "install-cli", label: "With the CLI", level: 2 },
  { id: "install-manual", label: "By hand", level: 2 },
  { id: "configure", label: "Configuration" },
] as const;

export function DocsPage({ children }: { children: React.ReactNode }) {
  return (
    <div className="page">
      <article>{children}</article>
      <aside className="toc">
        {/* Sticky header is 64px tall: headings count as reached just below it. */}
        <TocProgress items={[...headings]} offset={88} onNavigate={id => history.replaceState(null, "", `#${id}`)} />
      </aside>
    </div>
  );
}

API reference

TocProgress

A table of contents that reads along with you. A thin line runs down the headings and bends through their indentation with a smooth S between levels. The part you have read fills in with the accent as you scroll, section by section, and a marker rides the tip of the line, lighting each heading as it arrives. Choosing a heading scrolls the article there smoothly while the line traces the way, and the time left beside the title counts down, then reads Finished.

PropTypeDefaultDescription
items (required)TocProgressItem[]–Headings in document order, each { id, label, level?: 1 | 2 | 3 }. id is the id of the heading element in the article; level sets the lane the line bends into and the label indent.
scrollContainerRefObject<HTMLElement | null> | HTMLElement | null–The element that scrolls the article. Defaults to the nearest scrolling ancestor of the first heading, or the page when there is none.
offsetnumber96Distance in px from the top of the scroll viewport at which a heading counts as reached, usually the height of a sticky header plus a little air. Headings scroll to this line when chosen.
activeIdstring–The current heading, controlled. By default the current heading follows the reading position; the line keeps following the scroll either way.
onActiveChange(id: string) => void–Called with the heading id when the marker reaches a new heading, while reading or while a chosen heading scrolls into place.
onNavigate(id: string) => void–Called with the heading id when a heading in the list is chosen, before the article scrolls. Use it to update the URL hash or analytics.
readingTimenumber–Reading time of the whole article in minutes. Defaults to an estimate from the words in the scroll container at 230 words a minute.
titlestring"On this page"Heading above the list and the accessible name of the navigation. Pass an empty string to hide it.
showTimeLeftbooleantrueShows the minutes left beside the title, rolling to each new value and ending on Finished with a check.
labelstring"On this page"Accessible name of the navigation when title is empty.
classNamestring–Class on the root element. Set --toc-accent to recolor the read line and the marker.
styleCSSProperties–Inline styles on the root element.

Keyboard interactions

KeysAction
TabMoves through the headings in the list; each is a link.
Arrow Down / Arrow UpMoves focus to the next or previous heading in the list.
Home / EndMoves focus to the first or last heading in the list.
EnterScrolls the article to the heading and moves focus to it without a second scroll jump, so reading and tabbing continue from there.

Accessibility

Motion

Responsive behavior

Performance

Notes for AI

Also in navigation

Guidance for AI tools

Table of contents progress: A table of contents that reads along with you: a line bends through the nested headings, fills as you scroll, and carries a marker to the current section, with smooth jumps and a time-left countdown. Follow the declared prop types and do not invent props. Keep keyboard access, reduced motion support, and both light and dark themes intact when adapting it.

Full library index: https://ui.xzero.co.uk/llms.txt

Browse all components