Svelte Component

Table of Contents

Allows you to quickly navigate the hierarchy of headings for the current page.

typescript
import { TableOfContents, tocCrawler } from '@skeletonlabs/skeleton';
Source Page Source

Demo

How It Works

Heading IDs

Each page heading requires a unique ID that acts as the scroll target for inner-page navigation.

html
<h2 class="h2" id="how-it-works">How it Works</h2>

Anchor Links

Each link within the Table of Contents then points to the matching target ID as shown below. Note the use of the #. When clicked, the browser will automatically scroll so that the targeted element is at the top of the visible screen.

html
<a href="#how-it-works">How It Works</a>

Settings

Automatic IDs

Set mode: generate to enable tocCrawler to automatically generate and set unique IDs for all headings that are descendents of the element the action is applied to.

html
<div use:tocCrawler={{ mode: 'generate' }}>

See the example below. Note this will not overwrite IDs you have set manually.

html
<!-- Before: -->
<h2 class="h2">Title One</h2>
<h2 class="h2" id="my-custom-id">Title Two</h2>

<!-- After: -->
<h2 class="h2" id="title-one">Title One</h2>
<h2 class="h2" id="my-custom-id">Title Two</h2>

Prefixes and Suffixes

We recommend setting a custom heading (per the instruction above) if a conflict is found within your page. However, you may also hardcode a prefix or suffix to all generated IDs as follows:

html
<div use:tocCrawler={{ mode: 'generate', prefix: 'foo', suffix: 'bar' }}>

<!-- Ex: foo-title-one-bar -->
<!-- Ex: foo-title-two-bar -->

Ignore Headings

To ignore a heading in the target region, append a data-toc-ignore attribute. The crawler will skip this.

html
<h2 class="h2" data-toc-ignore>Ignore Me</h2>

Invisible Headings

Use the Tailwind-provided Screen Reader .sr-only class to append an invisible heading.

html
<h2 class="sr-only">Include Me!</h2>

Keyed Updates

In some situations you may want to force the crawler action to update on demand. Use the key parameter and pass a value that will be modified. This operates similar to Svelte's key blocks.

typescript
const tabIndex = 0;
html
<div use:tocCrawler={{ key: tabIndex }}>

Active on Scroll

The tocCrawler action can automatically select the top visible heading when you supply a scrollTarget element. That being the element that handles scrolling for the page. By default, this is set to target the body element. When using the Skeleton App Shell, designate scrollTarget: '#page' element as shown below. To disable this feature, set scrollTarget: ''.

NOTE: depending on your page layout, the page may not scroll low enough to activate the final links in the list.
html
<div use:tocCrawler={{ scrollTarget: '#page' }}>

Dynamic Attributes

Generating links constructed from dynamic attributes may result in unexpected behavior.

Example

html
<h2 class="h2">Greetings {name}</h2>

Svelte will compile this header to:

html
<h2 class="h2">"Greetings " "skeleton"</h2>

which means the header now has two children: "Greetings " and "skeleton".

Since the Table of Contents targets only the first child to avoid including any icons, etc., in the link, we end up with links missing parts of the text.

Solution

Using string interpolation solves the problem, by updating the code to:

html
<h2 class="h2">{`Greetings ${name}`}</h2>

The component will be compiled to:

html
<h2 class="h2">"Greetings skeleton"</h2>

Styling

Smooth Scrolling

Use Tailwind's scroll behavior styles to enable smooth scrolling on the scrollable element.

Make considerations for the Reduced Motion settings for proper accessability.
html
<!-- If using the App Shell: -->
<AppShell regionPage="scroll-smooth"></AppShell>

<!-- If NOT using the App Shell: -->
<body class="scroll-smooth"></body>

Scroll Margin

Use Tailwind's scroll margin styles if you need to offset for sticky headers

NOTE: not currently supported for Skeleton's App Shell.
html
<body class="scroll-mt-[100px]"></body>

Sticky Positioning

Use Tailwind's sticky positioning styles to keep the Table of Contents component visible while scrolling.

html
<TableOfContents class="sticky top-10" />