Toc
An on-this-page table of contents that highlights the section currently in view.
Live demo
Toc lists the sections of a page as in-page links and tracks which one the reader is looking at. Give it items: each is an id matching a section's element id, a label, and an optional 1-based level, and it renders a labelled nav of anchor links.
Entries deeper than the one before them nest inside it as a real sublist, so a two-level outline is structural rather than an indent an assistive reader can't see; a level that skips a depth is treated as one step down, which is what heading sources actually emit. An IntersectionObserver then marks the active link as you scroll, setting aria-current and the accent rail. The links work as plain anchor jumps with no script, and a clicked section still lights its entry without one, so the scrollspy is pure progressive enhancement. It reads as a vertical rail beside the content and folds into a wrapped row of chips on narrow screens. Pass sticky to keep it in view as the page scrolls.
When to use
How this component composes with the rest of the set.
Props
3 props, straight from the manifest.
| Prop | Type | Default | Bindings | Description |
|---|---|---|---|---|
Appearance
States
active
The link for the section currently in view: accent ink and rail, set alongside aria-current.
focus-visible
Keyboard focus on a link: a token ring plus a transparent outline that becomes real in forced-colors mode.
Anatomy
The named parts that make up the component, with their selectors.
toc
The nav root carrying the rail border and (optional) sticky positioning.
label
The small uppercase heading above the list.
nested list
A sublist of deeper entries, nested inside the li of the section it belongs to.
link
A section link with its accent rail when active and a focus ring.
Tokens & coverage
What the component consumes, checked live against what the algorithm produces.
Live coverage check against the xtyle-default register
(derive(xtyleDefault, { anchors }) →
coverComponent(manifest, register)). Every token this component
consumes must be a key the algorithm produces.
--accent
--accent-bg
--accent-text
--bg-1
--border-normal
--border-thick
--border-thin
--duration-fast
--ease-standard
--fg-0
--fg-2
--fg-3
--font-sans
--line
--radius-md
--radius-sm
--ring
--space-1
--space-2
--space-3
--space-4
--space-5
--text-sm
--text-xs
--weight-medium
--weight-semibold
Accessibility
Code
A scrollspy table of contents
A sticky rail of section links that tracks the section in view; entries carrying a deeper level nest under the one above them, and on narrow screens the whole thing folds into a wrapped row.
<xtyle-toc
label="On this page"
sticky
items='[{"id":"intro","label":"Intro"},{"id":"usage","label":"Usage"},{"id":"options","label":"Options","level":2},{"id":"api","label":"API"}]'
></xtyle-toc>
<script lang="ts">
import { Toc } from "@xtyle/svelte";
const items = [
{ id: "intro", label: "Intro" },
{ id: "usage", label: "Usage" },
{ id: "options", label: "Options", level: 2 },
{ id: "api", label: "API" },
];
</script>
<Toc {items} label="On this page" sticky />
---
import { Toc } from "@xtyle/astro";
// getHeadings() returns { depth, slug, text } per heading, which maps straight across
const items = Astro.props.headings.map((h) => ({ id: h.slug, label: h.text, level: h.depth - 1 }));
---
<Toc items={items} label="On this page" sticky />