Skip to main content

Toc

Info:Navigation html svelte astro Success: coverage 24/24

An on-this-page table of contents that highlights the section currently in view.

Live demo

live · @xoji/astro

Toc

Introduction

Scroll this column and watch the active link track the heading in view. Every entry is a real anchor, so it jumps on click and works with no script at all.

Scroll this column and watch the active link track the heading in view. Every entry is a real anchor, so it jumps on click and works with no script at all.

Anchors

Scroll this column and watch the active link track the heading in view. Every entry is a real anchor, so it jumps on click and works with no script at all.

Scroll this column and watch the active link track the heading in view. Every entry is a real anchor, so it jumps on click and works with no script at all.

Knobs

Scroll this column and watch the active link track the heading in view. Every entry is a real anchor, so it jumps on click and works with no script at all.

Scroll this column and watch the active link track the heading in view. Every entry is a real anchor, so it jumps on click and works with no script at all.

Coverage

Scroll this column and watch the active link track the heading in view. Every entry is a real anchor, so it jumps on click and works with no script at all.

Scroll this column and watch the active link track the heading in view. Every entry is a real anchor, so it jumps on click and works with no script at all.

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 plus a label, and it renders a labelled nav of anchor links.

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, 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.

Place it in a sidebar column beside the content; pass sticky so it follows the scroll.
Point each item.id at a section's element id so the in-page anchor and the scrollspy target line up.
Pair with Heading sections that carry matching ids; the Toc is the map, the headings are the territory.

Props

3 props, straight from the manifest.

PropTypeDefaultBindingsDescription
items TocItem[]
html svelte astro
The sections, each `{ id, label }`; `id` matches the target element's id, `label` is the link text. On the custom element, a JSON string.
label string On this page
html svelte astro
The heading above the list.
sticky boolean false
html svelte astro
Pins the nav with `position: sticky` so it stays in view while the page scrolls.

Appearance

States

active

.xoji-toc__link.is-active

The link for the section currently in view: accent ink and rail, set alongside aria-current.

focus-visible

.xoji-toc__link: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

.xoji-toc

The nav root carrying the rail border and (optional) sticky positioning.

--space-3 --space-2 --space-5 --border-thin --line --radius-md --bg-1

label

.xoji-toc__label

The small uppercase heading above the list.

--font-sans --text-xs --weight-semibold --fg-2 --space-2

link

.xoji-toc__link

A section link with its accent rail when active and a focus ring.

--font-sans --text-sm --fg-2 --fg-0 --border-thick --accent-text --accent --weight-medium --duration-fast --ease-standard --border-normal --ring --radius-sm --accent-bg

Tokens & coverage

What the component consumes, checked live against what the algorithm produces.

Success:fully covered 24/24 consumed tokens produced default register: 276 tokens

Live coverage check against the xoji-default register (derive(xojiDefault, { 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 --font-sans --line --radius-md --radius-sm --ring --space-1 --space-2 --space-3 --space-5 --text-sm --text-xs --weight-medium --weight-semibold

Accessibility

The nav names itself (aria-label) and the active link carries aria-current, so assistive tech announces which section is in view.
Every entry is a real in-page anchor link, so the table of contents works fully without JavaScript; the scrollspy only decorates it.
Links show keyboard focus with a focus-visible ring plus a transparent outline the forced-colors base rule promotes to a real system outline.

Code

A scrollspy table of contents

A sticky rail of section links that tracks the section in view; on narrow screens it folds into a wrapped row.

<xoji-toc
	label="On this page"
	sticky
	items='[{"id":"intro","label":"Intro"},{"id":"usage","label":"Usage"},{"id":"api","label":"API"}]'
></xoji-toc>
<script lang="ts">
	import { Toc } from "@xoji/svelte";

	const items = [
		{ id: "intro", label: "Intro" },
		{ id: "usage", label: "Usage" },
		{ id: "api", label: "API" },
	];
</script>

<Toc {items} label="On this page" sticky />
---
import { Toc } from "@xoji/astro";

const items = [
	{ id: "intro", label: "Intro" },
	{ id: "usage", label: "Usage" },
	{ id: "api", label: "API" },
];
---

<Toc items={items} label="On this page" sticky />