Skip to content
uiify

Table of contents

Compact heading navigation with hover magnification and scrollspy.

Browsers
Chrome 123+Firefox 121+Safari 17.5+
Tier
2
Bundle compressed
4.14 kB
Forced layout budget
0 ms
Axe budget
0
SSR compatible
Yes
Published contract
Test harness

Playground

Overview

A table of contents gives readers a quick map of a long page. Follow a heading to jump directly to its section, or keep reading and let the current position update as you scroll.

Installation

Import the client entry to enable scroll tracking and pointer interaction. The native button and heading links are already usable before the page finishes loading.

Configuration

Match every line and link to a unique heading ID. Choose a scroll container and a content boundary when the article lives inside another page.

Examples

Hover a line to see its heading in the contents list. Click the line to jump there. On a touch screen, open the list and select the heading you want to read.

Accessibility

The contents are ordinary navigation links. Use the keyboard to open the list, move through its headings, and dismiss it with Escape. Scrolling never moves keyboard focus.

Styling

Adjust line length, spacing and the panel width with CSS variables. The current section uses bolder text; the heading under the pointer gets a separate hover background.

Conclusion

The same component works beside a document or inside a reader. Its compact rail stays available while the article scrolls, without taking a separate content column.

API

TableOfContents.Root (/client)

PropTypeDefaultDescription
scrollSpybooleantrueTrack same-page targets in document order after hydration.
scrollTargetstring—Scrollable element ID. Omit to track the owning window; a missing ID does not fall back.
contentTargetstring—Content element ID, limiting target discovery and determining the article end.
offsetnumber0Pixels below the scroll viewport start used to select the current heading.
valuestring | null—Controlled current target ID. null clears selection.
defaultValuestring | nullnullInitial current target ID.
onValueChange(value: string | null) => void—Reports scrollspy changes without moving focus.
openboolean—Controlled popover state.
defaultOpenbooleanfalseInitial open state applied after hydration.
onOpenChange(open: boolean) => void—Reports hover, activation and native dismissal requests.
openOnHoverbooleantrueOpen on mouse hover; touch uses the native button.
openDelaynumber100Mouse hover opening delay in milliseconds.
closeDelaynumber200Grace period for moving between trigger and list.
renderReactElement | ((props, state) => ReactElement)—Replace the rendered element while keeping behavior and accessibility wiring.

TableOfContents.Trigger

PropTypeDefaultDescription
target*string—Unique Content ID, wired with native popovertarget.
aria-labelstring—Accessible name required when the trigger contains only the decorative rail.

TableOfContents.Indicator

PropTypeDefaultDescription
value*string—Target ID matching a Link fragment; one indicator per section.
level1 | 2 | 3 | 4 | 5 | 62Heading depth available through --uiify-toc-level.
selectedbooleanfalseServer entry selection; client derives selection from Root.value.
renderReactElement | ((props, state) => ReactElement)—Replace the rendered element while keeping behavior and accessibility wiring.

TableOfContents.Content

PropTypeDefaultDescription
id*string—Unique popover ID matching Trigger.target.
side"top" | "right" | "bottom" | "left""left"Native CSS anchor side; layout flips when necessary.
align"start" | "center" | "end""start"Alignment beside the trigger.
renderReactElement | ((props, state) => ReactElement)—Replace the rendered element while keeping behavior and accessibility wiring.

TableOfContents.Link

TableOfContents.Rail / List

PropTypeDefaultDescription
renderReactElement | ((props, state) => ReactElement)—Replace the rendered element while keeping behavior and accessibility wiring.

Keyboard

KeyAction
Enter / SpaceToggle the native contents popover from its trigger
Tab / Shift+TabMove through ordinary heading links
EscapeDismiss the popover; return focus from the list to its trigger

Accessibility

Label the navigation landmark and its icon-only trigger. The server entry supports native popover activation, dismissal and fragment links before hydration; the /client entry adds hover, click pinning, magnification and scrollspy. Current links expose aria-current=location and decorative indicators are hidden from assistive technology. Scrollspy never moves focus or announces scrolling. With reduced motion, the optional skin disables line transitions.

Styling hooks

data-uiify-table-of-contentsdata-partdata-selecteddata-highlighteddata-statedata-sidedata-aligndata-positioning--uiify-toc-line-size--uiify-toc-line-max-size--uiify-toc-line-thickness--uiify-toc-row-size--uiify-toc-magnify-radius--uiify-toc-indent--uiify-toc-panel-size--uiify-toc-duration--uiify-toc-level--uiify-toc-index--uiify-toc-pointer