
# Table of Contents

**A table of contents that highlights the headings you are reading.**

**Reference:** [View Source](https://github.com/jramke/fluid-primitives/tree/main/Resources/Private/Primitives/Toc) · [Zag.js Docs](https://zagjs.com/components/react/toc)

**TocExamples.html**

```html
<f:variable name="headings" value="{
    0: {value: 'toc-simple-delivery', depth: 2, label: 'Delivery', text: 'Orders placed before 2 pm on a working day ship the same day. You get a tracking link by email once the parcel leaves the warehouse, and you can follow it until the courier hands it over at your door.'},
    1: {value: 'toc-simple-returns', depth: 2, label: 'Returns', text: 'You can return unworn items within 30 days. Print the return label from your order confirmation and drop the parcel at any post office. Keep the receipt of the post office until your refund has arrived.'},
    2: {value: 'toc-simple-payment', depth: 2, label: 'Payment', text: 'We accept credit card, PayPal and invoice. Your card is charged when the order ships, not when you place it, so an item that is out of stock never costs you anything.'},
    3: {value: 'toc-simple-contact', depth: 2, label: 'Contact', text: 'Write to support@example.com or call us on weekdays between 9 am and 5 pm. We answer emails within one working day, and a call is quicker for questions about a parcel on its way.'}
}" />

<div class="flex h-56 w-full max-w-xl gap-8">
    <div id="toc-simple-content" class="min-w-0 flex-1 overflow-y-auto rounded-md border px-4">
        <f:for each="{headings}" as="heading">
            <section class="min-h-28 py-3">
                <h{heading.depth} id="{heading.value}" class="font-semibold text-foreground">{heading.label}</h{heading.depth}>
                <p class="mt-2 text-sm text-muted-foreground">{heading.text}</p>
            </section>
        </f:for>
    </div>
    <ui:toc.root items="{headings}" scrollEl="#toc-simple-content" autoScroll="{false}" class="w-36 shrink-0">
        <ui:toc.title>On this page</ui:toc.title>
        <ui:toc.list>
            <f:for each="{headings}" as="heading">
                <ui:toc.item value="{heading.value}"><ui:toc.link>{heading.label}</ui:toc.link></ui:toc.item>
            </f:for>
        </ui:toc.list>
    </ui:toc.root>
</div>

```

## Features

- Highlights the links of the headings that are visible, tracked with an `IntersectionObserver`
- Highlights several links at once when several headings are visible
- Scrolls its own list so the first active link stays in view
- Follows the headings of a scroll container as well as of the page
- Sets `aria-current="location"` on the active links
- Exposes the position of the active links for an animated indicator

## Anatomy

```html
<primitives:toc.root>
    <primitives:toc.title />
    <primitives:toc.list>
        <primitives:toc.indicator />
        <primitives:toc.item>
            <primitives:toc.link />
        </primitives:toc.item>
    </primitives:toc.list>
</primitives:toc.root>
```

## Installation

```bash
typo3 ui:add toc
```

Or copy the files manually from GitHub (https://github.com/jramke/fluid-primitives/tree/main/Resources/Private/Registry/Toc) into your project.

Read more about installing [Components and Primitives](/docs/core-concepts/primitives.md).

## Examples

### Build the items from your content

`items` takes `TocItem` objects or arrays with a `value` (the id of the heading element) and a `depth`. A `TocItem` also carries the `label` for your template and for client scripts.

```php
use Jramke\FluidPrimitives\Domain\Dto\TocItem;

$items = array_map(
    static fn(array $header) => new TocItem(value: 'c' . $header['uid'], depth: 2, label: $header['header']),
    $contentElements,
);
```

```html
<ui:toc.root items="{items}">
    <ui:toc.list>
        <f:for each="{items}" as="item">
            <ui:toc.item value="{item.value}"><ui:toc.link>{item.label}</ui:toc.link></ui:toc.item>
        </f:for>
    </ui:toc.list>
</ui:toc.root>
```

### Indent nested headings

List the `depth` of each heading in `items`: `2` for an `h2`, `3` for an `h3`. An item exposes it as the `--depth` CSS variable, which the `ui:toc.item` of the registry turns into an indent.

**TocExamples.html**

```html
<f:variable name="headings" value="{
    0: {value: 'toc-nested-delivery', depth: 2, label: 'Delivery', text: 'Orders placed before 2 pm on a working day ship the same day. You get a tracking link by email once the parcel leaves the warehouse, and you can follow it until the courier hands it over at your door.'},
    1: {value: 'toc-nested-delivery-times', depth: 3, label: 'Delivery times', text: 'Parcels reach most addresses in Germany within two working days, the rest of Europe takes up to five. Islands and remote regions can take a day or two longer, which the tracking link shows.'},
    2: {value: 'toc-nested-shipping-costs', depth: 3, label: 'Shipping costs', text: 'Shipping is free from 50 EUR. Below that it costs 4.90 EUR within Germany and 9.90 EUR for the rest of Europe. The costs show in your basket before you pay.'},
    3: {value: 'toc-nested-returns', depth: 2, label: 'Returns', text: 'You can return unworn items within 30 days. Print the return label from your order confirmation and drop the parcel at any post office. Keep the receipt of the post office until your refund has arrived.'},
    4: {value: 'toc-nested-refunds', depth: 3, label: 'Refunds', text: 'We refund the price of the items to the payment method you used. The shipping costs come back too if you return the whole order, otherwise they stay with the items you keep.'},
    5: {value: 'toc-nested-refund-times', depth: 4, label: 'Refund times', text: 'A refund arrives within five working days after we receive the parcel. You get an email as soon as we have booked it, and your bank may need a few more days to show it.'},
    6: {value: 'toc-nested-contact', depth: 2, label: 'Contact', text: 'Write to support@example.com or call us on weekdays between 9 am and 5 pm. We answer emails within one working day, and a call is quicker for questions about a parcel on its way.'}
}" />

<div class="flex h-64 w-full max-w-xl gap-8">
    <div id="toc-nested-content" class="min-w-0 flex-1 overflow-y-auto rounded-md border px-4">
        <f:for each="{headings}" as="heading">
            <section class="min-h-28 py-3">
                <h{heading.depth} id="{heading.value}" class="font-semibold text-foreground">{heading.label}</h{heading.depth}>
                <p class="mt-2 text-sm text-muted-foreground">{heading.text}</p>
            </section>
        </f:for>
    </div>
    <ui:toc.root items="{headings}" scrollEl="#toc-nested-content" rootMargin="-20px 0% -30% 0%" autoScroll="{false}" class="w-44 shrink-0">
        <ui:toc.title>On this page</ui:toc.title>
        <ui:toc.list>
            <f:for each="{headings}" as="heading">
                <ui:toc.item value="{heading.value}"><ui:toc.link>{heading.label}</ui:toc.link></ui:toc.item>
            </f:for>
        </ui:toc.list>
    </ui:toc.root>
</div>

```

### Follow the active link with an indicator

Place a `ui:toc.indicator` in the list. It follows the active links through the `--top`, `--height`, `--left` and `--width` CSS variables of the root, and the list is its positioning context.

**TocExamples.html**

```html
<f:variable name="headings" value="{
    0: {value: 'toc-indicator-delivery', depth: 2, label: 'Delivery', text: 'Orders placed before 2 pm on a working day ship the same day. You get a tracking link by email once the parcel leaves the warehouse, and you can follow it until the courier hands it over at your door.'},
    1: {value: 'toc-indicator-returns', depth: 2, label: 'Returns', text: 'You can return unworn items within 30 days. Print the return label from your order confirmation and drop the parcel at any post office. Keep the receipt of the post office until your refund has arrived.'},
    2: {value: 'toc-indicator-refunds', depth: 3, label: 'Refunds', text: 'We refund the price of the items to the payment method you used. The shipping costs come back too if you return the whole order, otherwise they stay with the items you keep.'},
    3: {value: 'toc-indicator-payment', depth: 2, label: 'Payment', text: 'We accept credit card, PayPal and invoice. Your card is charged when the order ships, not when you place it, so an item that is out of stock never costs you anything.'},
    4: {value: 'toc-indicator-contact', depth: 2, label: 'Contact', text: 'Write to support@example.com or call us on weekdays between 9 am and 5 pm. We answer emails within one working day, and a call is quicker for questions about a parcel on its way.'}
}" />

<div class="flex h-56 w-full max-w-xl gap-8">
    <div id="toc-indicator-content" class="min-w-0 flex-1 overflow-y-auto rounded-md border px-4">
        <f:for each="{headings}" as="heading">
            <section class="min-h-28 py-3">
                <h{heading.depth} id="{heading.value}" class="font-semibold text-foreground">{heading.label}</h{heading.depth}>
                <p class="mt-2 text-sm text-muted-foreground">{heading.text}</p>
            </section>
        </f:for>
    </div>
    <ui:toc.root items="{headings}" scrollEl="#toc-indicator-content" autoScroll="{false}" class="w-44 shrink-0">
        <ui:toc.title>On this page</ui:toc.title>
        <ui:toc.list class="border-s">
            <ui:toc.indicator class="-start-px" />
            <f:for each="{headings}" as="heading">
                <ui:toc.item value="{heading.value}"><ui:toc.link class="ps-3">{heading.label}</ui:toc.link></ui:toc.item>
            </f:for>
        </ui:toc.list>
    </ui:toc.root>
</div>

```

### Draw a rail

Each link draws a segment of the rail beside it, and the segment of an active link turns dark. A heading one level deeper steps the rail in, with a curve where the level changes.

**TocExamples.html**

```html
<f:variable name="headings" value="{
    0: {value: 'toc-rail-delivery', depth: 2, label: 'Delivery', text: 'Orders placed before 2 pm on a working day ship the same day. Later orders leave the warehouse the next morning, and we do not ship on Sundays or public holidays.'},
    1: {value: 'toc-rail-returns', depth: 2, label: 'Returns', text: 'You can return unworn items within 30 days. They need their tags and the original packaging, and we check them when the parcel arrives at our warehouse.'},
    2: {value: 'toc-rail-return-label', depth: 3, label: 'Return label', text: 'Print the label from your order confirmation and stick it on the parcel. If you have no printer, show the QR code in the email at any post office and they print it for you.'},
    3: {value: 'toc-rail-return-window', depth: 3, label: 'Return window', text: 'The 30 days start on the day the parcel arrives, not on the day you ordered. Send the items back within that time, and we still accept them if the parcel is on its way.'},
    4: {value: 'toc-rail-payment', depth: 2, label: 'Payment', text: 'We accept credit card, PayPal and invoice. You pick one at the end of the checkout, and you can change it as long as the order has not shipped.'},
    5: {value: 'toc-rail-credit-card', depth: 3, label: 'Credit card', text: 'Your card is charged when the order ships, not when you place it. If the payment fails, we email you a link to try another card before we cancel anything.'},
    6: {value: 'toc-rail-invoice', depth: 3, label: 'Invoice', text: 'You get the invoice with the parcel and a second copy by email. Invoice payment is available from your second order, and only for orders up to 500 EUR.'},
    7: {value: 'toc-rail-due-date', depth: 4, label: 'Due date', text: 'Pay within 14 days of the invoice date. The bank details are on the invoice, so please add the invoice number as the reference, otherwise we cannot match your payment.'},
    8: {value: 'toc-rail-contact', depth: 2, label: 'Contact', text: 'Write to support@example.com on weekdays and we answer within one working day. Please add your order number, so we can find your order without asking again.'}
}" />

<div class="flex h-72 w-full max-w-xl gap-8">
    <div id="toc-rail-content" class="min-w-0 flex-1 overflow-y-auto rounded-md border px-4">
        <f:for each="{headings}" as="heading">
            <section class="min-h-28 py-3">
                <h{heading.depth} id="{heading.value}" class="font-semibold text-foreground">{heading.label}</h{heading.depth}>
                <p class="mt-2 text-sm text-muted-foreground">{heading.text}</p>
            </section>
        </f:for>
    </div>
    <ui:toc.root items="{headings}" scrollEl="#toc-rail-content" rootMargin="-20px 0% -30% 0%" autoScroll="{false}" class="w-48 shrink-0">
        <ui:toc.title>On this page</ui:toc.title>
        <ui:toc.list>
            <f:for each="{headings}" as="heading" iteration="i">
                <f:comment>The rail of a link is drawn from the depth of its neighbours: it bends where the depth changes. The dark copy of the rail is clipped away below the active items (from the bottom) and above them (from the top), so it draws in and out along the direction you scroll.</f:comment>
                <f:variable name="before" value="{i.index - 1}" />
                <f:variable name="after" value="{i.index + 1}" />
                <f:variable name="prev" value="{headings.{before}}" />
                <f:variable name="next" value="{headings.{after}}" />
                <f:variable name="prevDepth" value="{heading.depth}" />
                <f:variable name="nextDepth" value="{heading.depth}" />
                <f:if condition="{prev}"><f:variable name="prevDepth" value="{prev.depth}" /></f:if>
                <f:if condition="{next}"><f:variable name="nextDepth" value="{next.depth}" /></f:if>
                <f:variable name="x" value="{heading.depth * 8}" />
                <f:variable name="prevX" value="{prevDepth * 8}" />
                <f:variable name="turns" value="{f:if(condition: '{prevDepth} != {heading.depth}', then: 1, else: 0)}" />
                <f:variable name="svgClass" value="pointer-events-none absolute -start-2 -top-1.5 w-10 {f:if(condition: '{nextDepth} == {heading.depth}', then: 'h-[calc(100%+6px)]', else: 'h-full')}" />
                <f:variable name="shape">
                    <f:if condition="{turns}"><path d="M {prevX}.5 0 C {prevX}.5 8 {x}.5 4 {x}.5 12" fill="none" /></f:if>
                    <line x1="{x}.5" y1="{f:if(condition: turns, then: 12, else: 6)}" x2="{x}.5" y2="100%" />
                </f:variable>

                <ui:toc.item value="{heading.value}" class="!ps-0 [--c:inset(0_0_100%_0)] data-[active]:[--c:inset(0)] [&:not([data-active]):has(~[data-active])]:[--c:inset(100%_0_0_0)]">
                    <ui:toc.link class="relative pe-2 ps-[calc(var(--depth)*12px-4px)]">
                        <svg class="{svgClass} stroke-border" aria-hidden="true">{shape -> f:format.raw()}</svg>
                        <svg class="{svgClass} stroke-foreground [clip-path:var(--c)] transition-[clip-path] duration-200 motion-reduce:transition-none" aria-hidden="true">{shape -> f:format.raw()}</svg>
                        {heading.label}
                    </ui:toc.link>
                </ui:toc.item>
            </f:for>
        </ui:toc.list>
    </ui:toc.root>
</div>

```

### Collapse the list into a summary

Put the list into a `ui:collapsible`. The trigger shows the active heading and how far you are, so the list can stay closed. The script mounts the `Toc` itself and updates the summary from `onActiveChange`: the `--progress` of the ring, the number and the label of the active heading.

**TocExamples.html**

```html
<f:variable name="headings" value="{
    0: {value: 'toc-collapsible-ingredients', depth: 2, label: 'Ingredients', text: 'Mix 500 g flour, 10 g salt, 7 g dry yeast and 350 ml lukewarm water. The dough is sticky, so keep a bowl of water nearby to wet your hands. Use bread flour if you can, it holds the shape better.'},
    1: {value: 'toc-collapsible-kneading', depth: 2, label: 'Kneading', text: 'Knead for ten minutes until the dough stops sticking to your hands, then let it rest for an hour. It should double in size, and small bubbles show on the surface when it is ready.'},
    2: {value: 'toc-collapsible-shaping', depth: 2, label: 'Shaping', text: 'Fold the dough into a tight ball, place it seam side up in a floured basket and cover it. Let it rise for another hour, until a finger pressed into it leaves a dent that fills back slowly.'},
    3: {value: 'toc-collapsible-baking', depth: 2, label: 'Baking', text: 'Bake at 240 C for 20 minutes with a pan of water on the rack below, then 20 minutes at 200 C. The crust should be deep brown, and the loaf sounds hollow when you tap its bottom.'},
    4: {value: 'toc-collapsible-storing', depth: 2, label: 'Storing', text: 'Let the loaf cool for an hour before cutting. It stays fresh for three days in a linen bag, and you can slice and freeze what you do not eat to toast it later.'}
}" />

<div class="flex h-72 w-full max-w-xl flex-col gap-3">
    <ui:toc.root items="{headings}" autoMount="{false}" rootId="{rootId}-toc" scrollEl="#toc-collapsible-content" autoScroll="{false}" class="w-full shrink-0 rounded-md border">
        <ui:collapsible.root>
            <ui:collapsible.trigger class="group flex w-full cursor-pointer items-center justify-between gap-3 p-3 text-start font-medium">
                <span class="flex min-w-0 items-center gap-3">
                    <span class="relative grid size-7 shrink-0 place-items-center" {ui:ref(name: 'ring' )}>
                        <svg viewBox="0 0 36 36" class="absolute inset-0 -rotate-90" aria-hidden="true">
                            <circle cx="18" cy="18" r="14" fill="none" stroke-width="2.5" class="stroke-border" />
                            <circle cx="18" cy="18" r="14" fill="none" stroke-width="2.5" stroke-linecap="round" class="stroke-foreground transition-[stroke-dasharray] duration-300 [stroke-dasharray:calc(var(--progress,0)*87.96)_87.96]" />
                        </svg>
                        <span class="text-[10px] font-semibold tabular-nums" {ui:ref(name: 'number' )}>-</span>
                    </span>
                    <span class="truncate" {ui:ref(name: 'label' )}>On this page</span>
                </span>
                <ui:icon.chevronDown class="size-4 shrink-0 transition-transform group-data-[state=open]:rotate-180" />
            </ui:collapsible.trigger>
            <ui:collapsible.content>
                <ui:toc.list class="px-3 pb-3">
                    <f:for each="{headings}" as="heading">
                        <ui:toc.item value="{heading.value}"><ui:toc.link>{heading.label}</ui:toc.link></ui:toc.item>
                    </f:for>
                </ui:toc.list>
            </ui:collapsible.content>
        </ui:collapsible.root>
    </ui:toc.root>
    <div id="toc-collapsible-content" class="min-h-0 flex-1 overflow-y-auto rounded-md border px-4">
        <f:for each="{headings}" as="heading">
            <section class="min-h-28 py-3">
                <h{heading.depth} id="{heading.value}" class="font-semibold text-foreground">{heading.label}</h{heading.depth}>
                <p class="mt-2 text-sm text-muted-foreground">{heading.text}</p>
            </section>
        </f:for>
    </div>
</div>

<vite:asset entry="EXT:docs/Resources/Private/Components/ui/TocExamples/Collapsible.entry.ts" />

```

**Collapsible.entry.ts**

```ts
import { mount, mountAll } from 'fluid-primitives';
import { Toc } from 'fluid-primitives/toc';

mountAll('ui:tocExamples.collapsible', ({ props, createHydrator }) => {
    const hydrator = createHydrator();
    const ring = hydrator.query('ring');
    const number = hydrator.query('number');
    const label = hydrator.query('label');
    if (!ring || !number || !label) return;

    mount('ui:toc', `${props.id}-toc`, ({ props }) => {
        const toc = new Toc({
            ...props,
            onActiveChange: ({ activeIds }) => {
                const index = toc.api.items.findIndex(item => item.value === activeIds[0]);
                ring.style.setProperty('--progress', String((index + 1) / toc.api.items.length));
                number.textContent = String(index + 1);
                label.textContent = toc.hydrator.queryAll('link')[index]?.textContent?.trim() ?? '';
            },
        });
        return toc.init();
    });
});

```

## API Reference

### toc.root

Wraps the table of contents, provides its state and takes the `items`. Renders a `<nav>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `items` | `array\|Domain\Dto\TocItem[]` | Yes | `-` | The headings of the page, a list of `TocItem` objects or of arrays with a `value` (the id of the heading) and a `depth` (its level, e.g. `2` for an `h2`). Every `ui:toc.item` takes its depth from here. |
| `defaultActiveIds` | `string[]` | No | `-` | The ids of the headings that are active when rendered, until the first observation replaces them. Use when you don't need to control the active headings. |
| `rootMargin` | `string` | No | `'-20px 0% -40% 0%'` | The root margin of the IntersectionObserver. It sets the area of the viewport in which a heading counts as visible. |
| `threshold` | `float\|float[]` | No | `0` | The threshold of the IntersectionObserver. `0` makes a heading active as soon as a single pixel of it is inside the root margin. |
| `scrollEl` | `string` | No | `-` | A CSS selector of the scroll container the headings sit in. Defaults to the viewport of the document. |
| `autoScroll` | `boolean` | No | `true` | Whether the table of contents scrolls the first active item into view when the active headings change. It scrolls every scrollable ancestor of the item, the page included, so turn it off for a list that does not scroll itself. |
| `scrollBehavior` | `Enum\ScrollBehavior` | No | `Smooth` | The scroll behavior of the auto-scroll of the list and of a click on a link. A link only scrolls by itself when `scrollEl` is set, otherwise the browser follows its hash. Smooth scrolling is skipped for a visitor who prefers reduced motion. |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `rootId` | `string` | No | `-` | The root ID of the component, used for hydration and identification. |
| `ids` | `array` | No | `[]` | The IDs of of the component parts for composition. |
| `autoMount` | `boolean` | No | `true` | Whether the client initializes the component on its own. Set it to false to mount it yourself with `mount()` |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |

### toc.title

Labels the table of contents. Renders an `<h2>` element, set `level` for another heading level.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `level` | `integer` | No | `2` | The heading level to render for the title. |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |

### toc.list

Wraps the items and is the positioning context of the indicator. Renders a `<ul>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |

### toc.item

One heading of the page, identified by its `value`. Renders an `<li>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | `string` | Yes | `-` | The id of the heading the item points to. It has to be one of the `items` of the root, which also set the depth of the item. The depth is available as the `--depth` CSS variable to indent the item. |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |

#### Rendered data attributes

| Attribute | Description |
| --- | --- |
| `data-toc-item` | <uid> |
| `data-value` | The value of the item |
| `data-depth` | The depth of the item |
| `data-active` | Present when active or pressed |
| `data-first` |  |
| `data-last` |  |

### toc.link

Links to the heading of its item. Renders an `<a>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `href` | `string` | No | `-` | The target of the link. Defaults to the hash of the item's heading, `#value`. |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |

#### Rendered data attributes

| Attribute | Description |
| --- | --- |
| `data-toc-link` | <uid> |
| `data-value` | The value of the item |
| `data-active` | Present when active or pressed |

### toc.indicator

Follows the active items. Renders a `<div>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |

### Machine JavaScript API

| Name | Type | Description |
| --- | --- | --- |
| `activeIds` | `string[]` | All currently active (visible) heading ids |
| `activeItems` | `TocItem[]` | The active (visible) TOC items |
| `items` | `TocItem[]` | The resolved items list |
| `setActiveIds` | `(value: string[]) => void` | Manually set the active heading ids |
| `scrollTo` | `(value: string, details?: ScrollToDetails \| undefined) => void` | Scrolls to the heading with the given id. |
| `getItemState` | `(props: ItemProps) => ItemState` | Returns the state of a TOC item |
| `getIndicatorState` | `() => IndicatorState` | Returns the state of the indicator |
