what it tracks
Scroll this page and watch the rails: nodes fill proportionally to how much of their block occupies the viewport — a half-visible heading block reads ~50%, not a binary in-view flag. Both directions saturate: cover the viewport entirely and the weight is 100%.
IoM weights
Intersection-over-minimum: the intersection area divided by the smaller of the block and the viewport. Tall blocks don't dwarf small ones — a short block fully visible weighs the same 100% as a tall block filling the screen.
the line pick
The bold marker is the viewport-top LINE: whichever region contains it is the pick. Margins between blocks resolve downward to the next block — the line never floats in dead space. On mobile the line sits at the sticky rail's bottom edge.
two modes, one family
MANUAL composes TocList (ul) / TocItem (li) / TocLink (a) — SSR-complete, nesting is a TocList inside a TocItem, anchors never nest. AUTO hands the rail a content root and the outline lib derives the same tree from the headings at runtime and renders through the same parts — the DECLARED SSR exception: the server paints the rail shell, links arrive on hydrate. The old sections[] prop is gone.
the engine
toc-engine.ts is framework-free (no framework imports — the engine owns the scroll/resize listener pair; the derivation lib toc-outline.ts is the listener-free half: DOM in, plain data out), and the family talks to it entirely through the DOM: the root re-queries its own subtree per update (no registration — keyed reorders and conditional links just work), derives each link's target from its href fragment, and synthesizes heading-to-heading extents for the engine. Scrollspy, aria-current, and the parent marker are root behavior in both modes.
<script lang="ts">
import Toc, { TocList, TocItem, TocLink } from '@ui/toc/index';
</script>
<!-- AUTO: derive from the content's headings (SSR paints the rail
shell; links arrive on hydrate — the declared exception).
PLACEMENT LAW: the content root cannot live inside a component
canvas — the canvas stamps data-toc-skip, and a skipped root
derives zero. -->
<aside class="jx-toc-aside">
<Toc outline={{ root: 'main' }} title="on this page" />
</aside>
<!-- MANUAL: the composed list tree — SSR-complete; nesting is a
TocList inside a TocItem, anchors never nest -->
<aside class="jx-toc-aside">
<Toc title="on this page">
<TocList>
<TocItem><TocLink href="#setup">Setup</TocLink></TocItem>
<TocItem>
<TocLink href="#usage">Usage</TocLink>
<TocList>
<TocItem><TocLink href="#theming">Theming</TocLink></TocItem>
</TocList>
</TocItem>
</TocList>
</Toc>
</aside>