Outline
A scroll-spy table of contents, built from scratch (no IntersectionObserver ambiguity — a heading is "active" once its top has scrolled past offset, the last such heading in document order wins).
Real use · scroll-spy docs page (scroll the panel below)
Installation
Placeholder copy for the “Installation” section — long enough to make the panel actually scroll so the active link updates as you go.
Via npm
Placeholder copy for the “Via npm” section — long enough to make the panel actually scroll so the active link updates as you go.
Via CDN
Placeholder copy for the “Via CDN” section — long enough to make the panel actually scroll so the active link updates as you go.
Usage
Placeholder copy for the “Usage” section — long enough to make the panel actually scroll so the active link updates as you go.
Theming
Placeholder copy for the “Theming” section — long enough to make the panel actually scroll so the active link updates as you go.
Color tokens
Placeholder copy for the “Color tokens” section — long enough to make the panel actually scroll so the active link updates as you go.
Dark mode
Placeholder copy for the “Dark mode” section — long enough to make the panel actually scroll so the active link updates as you go.
FAQ
Placeholder copy for the “FAQ” section — long enough to make the panel actually scroll so the active link updates as you go.
Using Outline
Silica ships one design system through three paths. They share a class vocabulary but are not interchangeable — pick the one that matches what you are building.
Plain HTML — CSS classes
The Tailwind v4 plugin. Works in any framework or none — Rails, Django, PHP, Go, a static file. This path ships no JavaScript, so Outline is styled but inert: its behavior comes from toc. For that behavior with no framework, use the node-tree path below.
/* in your CSS */
@import "tailwindcss";
@plugin "@wizeworks/silicaui";Root class outline, with 2 modifiers:
.outline.outline-link.outline-list
React
Typed components over those same classes, with behavior and accessibility from Base UI.
npm i @wizeworks/silicaui-reactimport { Outline } from "@wizeworks/silicaui-react";Props
| Prop | Type | Required | Description |
|---|---|---|---|
| items | OutlineItem[] | yes | — |
| offset | number | no | Distance in px from the scroll container's top edge that counts as the "active" boundary — a heading is current once it's scrolled to here. Default 96 (roughly a sticky header's height). |
| container | React.RefObject<HTMLElement | null> | no | Scrollable ancestor to track. Default: the window (a real full-page TOC). |
| onActiveChange | (id: string | null) => void | no | — |
Source of the demo above
The exact file that renders the examples above, so it can never drift from them. It imports a few of this site’s own layout helpers (../lib/…), so read it for the component calls rather than copying it whole.
Show source
import { useRef } from "react";
import { Outline } from "@wizeworks/silicaui-react";
import { Section } from "../lib/Section";
const SECTIONS = [
{ id: "outline-install", label: "Installation", depth: 0 },
{ id: "outline-npm", label: "Via npm", depth: 1 },
{ id: "outline-cdn", label: "Via CDN", depth: 1 },
{ id: "outline-usage", label: "Usage", depth: 0 },
{ id: "outline-theming", label: "Theming", depth: 0 },
{ id: "outline-tokens", label: "Color tokens", depth: 1 },
{ id: "outline-dark", label: "Dark mode", depth: 1 },
{ id: "outline-faq", label: "FAQ", depth: 0 },
];
export function OutlineDemo() {
const containerRef = useRef<HTMLDivElement>(null);
return (
<Section title="Real use · scroll-spy docs page (scroll the panel below)">
<div className="grid grid-cols-[1fr_12rem] gap-6">
<div
ref={containerRef}
className="h-80 overflow-y-auto rounded-box border border-base-300 p-6"
>
{SECTIONS.map((s) => (
<section key={s.id} id={s.id} className="mb-10 scroll-mt-4">
<h3 className="mb-2 text-base font-semibold">{s.label}</h3>
<p className="text-sm opacity-70">
Placeholder copy for the “{s.label}” section — long enough to
make the panel actually scroll so the active link updates as
you go.
</p>
</section>
))}
</div>
<Outline items={SECTIONS} container={containerRef} offset={24} />
</div>
</Section>
);
}Vanilla — node tree to HTML
Outline — toc; unlike the React version (which derives its list from scanning heading elements at runtime), the vanilla macro is prop-driven like Breadcrumb/Steps/Timeline: props.items: {id, label}[] becomes the anchor links the toc behavior tracks via IntersectionObserver. Build a node tree, project it with toHtml(), and let the zero-dependency runtime hydrate it. This is the path for generated or user-authored documents — a site builder, CMS output, a static export.
npm i @wizeworks/silicaui-html @wizeworks/silicaui-behaviorsimport { atom, toHtml } from "@wizeworks/silicaui-html";
const node = atom("Outline", "outline", { /* props */ });
const markup = toHtml(node);Styling on this path is the class string in the second argument — the same classes as the Plain HTML section above. There is no color prop here; write "outline outline-primary" rather than passing a colour as a prop.
- Takes children
- no
- Hydrated by
toc
Load @wizeworks/silicaui-behaviors on the page or this markup renders correctly and does nothing.
The full API for Outline, on every path, is available through the Silica MCP server.