Cloud|

@k2b/ui

TableOfContents

Native section links with a container or window scroll spy.

TableOfContents@k2b/ui

Section links follow this small scroll container. The navigation stays in view.

Sharing

Select Share to invite another reader.

Access

Choose who can read the document.

Copy the link.

TSX

Copy
const [root, setRoot] = createSignal<HTMLElement>();
const items = [
{ id: "toc-demo-sharing", label: "Sharing" },
{ id: "toc-demo-access", label: "Access" },
{ id: "toc-demo-links", label: "Links" },
];
<div ref={setRoot} style={{ height: "16rem", overflow: "auto", display: "grid", "grid-template-columns": "8rem 1fr", gap: "1rem" }}>
<TableOfContents items={items} scrollRoot={root} sticky />
<article>
<section style={{ "min-height": "14rem" }}><h2 id="toc-demo-sharing">Sharing</h2><p>Select Share to invite another reader.</p></section>
<section style={{ "min-height": "14rem" }}><h2 id="toc-demo-access">Access</h2><p>Choose who can read the document.</p></section>
<section style={{ "min-height": "7rem" }}><h2 id="toc-demo-links">Links</h2><p>Copy the link.</p></section>
</article>
</div>

TableOfContents links to sections and marks the current section as content scrolls.

Use TableOfContents

Use it beside a page or a scrollable article. Each item identifies an existing heading.

Import

tsx
import { TableOfContents } from "@k2b/ui";

Example

tsx
const [root, setRoot] = createSignal<HTMLElement>();
const items = [{ id: "sharing", label: "Sharing" }];

<TableOfContents items={items} scrollRoot={root} sticky />
<article ref={setRoot} style={{ height: "20rem", overflow: "auto" }}>
  <h2 id="sharing">Sharing</h2>
</article>

Items have an id and a text label. Empty items render nothing. The visible title and navigation name default to “On this page” or “Auf dieser Seite” from the inherited locale. Pass title to replace it.

Without scrollRoot, the window decides the active section. Pass an element or accessor for a content scroller. The last heading above a line one quarter down its visible box becomes active. Before the first heading reaches that line, the first item stays active. At the end of scrolling, a visible last heading becomes active. onActiveChange receives the new ID, or undefined when the list becomes empty, once per change. The active section also updates without scrolling when the scroll root, a direct child of it, or a heading's parent element changes size, for example after an image loads or a disclosure opens.

sticky keeps the navigation in view within the caller's layout. Links use normal browser anchor navigation. Changing the active section does not change the list's geometry. Server markup marks the first item current.

Accessibility

The visible heading names the nav. Native links remain keyboard accessible. Only the active link has aria-current="location".

Runtime

Native links work without JavaScript. The scroll spy starts in the browser and removes its listeners when disposed.

Connect a coding agent

The Cloud skill provides compact working instructions. MCP supplies exact current documentation when details matter.

CLI command
bunx skills add https://docs.example.com

This command installs the working instructions published by this website in the selected coding agent.

For exact, current details, also connect the MCP server through one of the agent tabs.

Installation uses the open-source Vercel Skills CLI.

The skill and MCP complement each other: the skill describes workflows, while MCP supplies current documentation.