Table of Contents (TOC)
Hierarchical navigation component that auto-generates from page headings with scroll spy functionality for improved document navigation.
Overview
The TOC component provides sticky sidebar navigation that automatically generates from your page's heading structure. It includes scroll spy to highlight the current section and smooth scrolling to sections when clicked.
Key features:
- Auto-generation from H2 and H3 headings
- Scroll spy with active section tracking
- Smooth scroll to sections on click
- Sticky positioning for persistent visibility
- Nested structure for heading hierarchy
- Mobile responsive (hidden by default on small screens)
- Fully themeable with CSS design tokens
Basic Example
Create a TOC container and initialize it with JavaScript to auto-generate navigation from your content headings.
← This is a static TOC example showing the component structure.
In real usage, the TOC auto-generates from your page headings using JavaScript.
<aside class="focus-toc" id="myTOC"></aside>
<script>
// Auto-generate TOC from page headings
FocusUI.TOC.init('#myTOC', {
contentSelector: '.content',
headingSelector: 'h2, h3',
title: 'On This Page'
});
</script>
Variants
Size Variants
Adjust TOC size with .focus-toc-sm or .focus-toc-lg classes.
<!-- Small -->
<aside class="focus-toc focus-toc-sm" id="smallTOC"></aside>
<!-- Large -->
<aside class="focus-toc focus-toc-lg" id="largeTOC"></aside>
Borderless Variant
Remove border and shadow with .focus-toc-borderless for a minimal look.
<aside class="focus-toc focus-toc-borderless" id="myTOC"></aside>
Compact Variant
Reduce spacing between items with .focus-toc-compact.
<aside class="focus-toc focus-toc-compact" id="myTOC"></aside>
Style Variants
Choose from 5 different visual styles for active state indicators.
Minimal
Pill
Dots
Line
Numbered
<!-- Left border indicator (default) -->
<aside class="focus-toc focus-toc-minimal" id="myTOC"></aside>
<!-- Background pill on active -->
<aside class="focus-toc focus-toc-pill" id="myTOC"></aside>
<!-- Dot indicators -->
<aside class="focus-toc focus-toc-dots" id="myTOC"></aside>
<!-- Vertical progress line -->
<aside class="focus-toc focus-toc-line" id="myTOC"></aside>
<!-- Auto-numbered entries -->
<aside class="focus-toc focus-toc-numbered" id="myTOC"></aside>
Common Patterns
Documentation Pages
Typical usage for documentation with sticky positioning and nested structure.
<div class="docs-layout">
<aside class="sidebar">
<!-- Main navigation -->
</aside>
<main class="content">
<article>
<h1>Page Title</h1>
<h2>Section 1</h2>
<p>Content...</p>
<h3>Subsection 1.1</h3>
<p>Content...</p>
<h2>Section 2</h2>
<p>Content...</p>
</article>
</main>
<aside class="focus-toc" id="pageTOC"></aside>
</div>
<script>
FocusUI.TOC.init('#pageTOC', {
contentSelector: '.content article',
headingSelector: 'h2, h3',
title: 'On This Page',
scrollOffset: 80
});
</script>
Long Articles
For blog posts or articles with a simple TOC showing only top-level headings.
<div style="max-width: 800px; margin: 0 auto;">
<aside class="focus-toc focus-toc-borderless" id="articleTOC"></aside>
<article>
<h1>Article Title</h1>
<h2>Introduction</h2>
<p>Content...</p>
<h2>Main Topic</h2>
<p>Content...</p>
<h2>Conclusion</h2>
<p>Content...</p>
</article>
</div>
<script>
FocusUI.TOC.init('#articleTOC', {
contentSelector: 'article',
headingSelector: 'h2', // Only H2, no nesting
title: 'Table of Contents'
});
</script>
Mobile Visibility
By default, TOC is hidden on mobile (<768px). Use .focus-toc-mobile to show it.
<!-- Hidden on mobile (default) -->
<aside class="focus-toc" id="desktopTOC"></aside>
<!-- Visible on mobile -->
<aside class="focus-toc focus-toc-mobile" id="mobileTOC"></aside>
Heading ID Generation
The TOC component automatically generates IDs for headings that don't have them, using a slug of the heading text.
// Heading without ID
<h2>Getting Started</h2>
// Automatically becomes
<h2 id="getting-started">Getting Started</h2>
// Special characters are removed
<h2>What's New in 2024?</h2>
// Becomes: id="whats-new-in-2024"
JavaScript API
The TOC component requires JavaScript for auto-generation and scroll spy functionality.
Initialize
FocusUI.TOC.init('#myTOC', {
contentSelector: '.content',
headingSelector: 'h2, h3',
title: 'On This Page',
scrollOffset: 100
})
Update
Regenerate TOC when headings change dynamically.
FocusUI.TOC.update('#myTOC')
Destroy
FocusUI.TOC.destroy('#myTOC')
Methods
| Method | Description |
|---|---|
TOC.init(selector, options) |
Initialize TOC with auto-generation and scroll spy |
TOC.update(selector) |
Regenerate TOC when headings change dynamically |
TOC.destroy(selector) |
Remove TOC and clean up event listeners |
Options
| Option | Type | Default | Description |
|---|---|---|---|
contentSelector |
string | 'body' |
Container to scan for headings |
headingSelector |
string | 'h2, h3' |
CSS selector for headings to include |
title |
string | 'On This Page' |
TOC heading title (empty string to hide) |
scrollOffset |
number | 100 |
Offset in pixels for scroll spy and smooth scroll |
smoothScroll |
boolean | true |
Enable smooth scrolling to sections on click |
activeClass |
string | 'focus-toc-link-active' |
CSS class for active/current link |
includeLevel |
array | [2, 3] |
Heading levels to include (e.g., [2, 3] for H2 and H3) |
Accessibility
The TOC component follows accessibility best practices:
- Semantic HTML: Uses
<nav>or<aside>with proper list structure - Keyboard Navigation: All links are focusable and keyboard accessible
- Focus Visible: Clear focus indicators on links
- ARIA Labels: Consider adding
aria-label="Table of Contents"to the container - Color Contrast: Active links use primary color with sufficient contrast
<aside class="focus-toc" id="myTOC" aria-label="Table of Contents"></aside>
CSS Classes Reference
Complete list of CSS classes used by the TOC component:
| Class | Description |
|---|---|
.focus-toc |
Main TOC container with sticky positioning |
.focus-toc-title |
TOC heading title (e.g., "On This Page") |
.focus-toc-list |
Top-level list container |
.focus-toc-item |
Individual list item wrapper |
.focus-toc-link |
Anchor link to section |
.focus-toc-link-active |
Active/current section highlight |
.focus-toc-nested |
Nested list for H3 under H2 |
.focus-toc-sm |
Small size variant |
.focus-toc-lg |
Large size variant |
.focus-toc-borderless |
Removes border and shadow |
.focus-toc-compact |
Reduces spacing between items |
.focus-toc-mobile |
Forces visibility on mobile screens |
.focus-toc-minimal |
Left border indicator style (default) |
.focus-toc-pill |
Background pill on active item |
.focus-toc-dots |
Dot indicators before each item |
.focus-toc-line |
Vertical progress line with highlighted segment |
.focus-toc-numbered |
Auto-numbered entries (01, 02, 03...) |
Browser Support
The TOC component works in all modern browsers that support:
- CSS custom properties (variables)
- ES6 JavaScript (classes, arrow functions, spread operator)
- Smooth scrolling API (
window.scrollTo({ behavior: 'smooth' })) - Sticky positioning (
position: sticky)
Tips and Best Practices
contentSelector to reduce the number of elements tracked by scroll spy.
FocusUI.TOC.update('#myTOC') to regenerate the TOC.