Endpoint Atlas
Glossar
Back to blog

Warum ich meine eigene Table of Contents gebaut habe (und wie sie funktioniert)

JU
Julian Wolf·Other·3 min read

Fast jede Dokumentation hat eine Table of Contents.

Und fast jede fühlt sich… irgendwie falsch an.

  • Sprünge zwischen Sections
  • Verzögerte Updates
  • Kein echtes Gefühl, wo man sich gerade im Dokument befindet

Technisch liegt das meist daran, dass alles auf IntersectionObserver basiert. Das funktioniert, aber es ist diskret statt kontinuierlich. Du bist nicht irgendwo zwischen zwei Sections du bist einfach in einer.

Ziel: Eine ToC, die sich wie Scrollen anfühlt

Ich wollte etwas anderes:

Die Navigation soll sich genauso flüssig anfühlen wie der Scroll selbst.

Also nicht:

Section wird aktiv → UI springt

Sondern:

Position im Dokument → direkt auf UI gemappt

Die Idee

Die gesamte ToC wird als SVG Path dargestellt.

  • Jeder Heading-Punkt wird auf eine Y-Position gemappt
  • Daraus entsteht eine durchgehende Linie
  • Die Scrollposition wird kontinuierlich darauf projiziert

Das Ergebnis:

  • Ein Track, der die gesamte Struktur zeigt
  • Ein Highlight, das genau den Viewport widerspiegelt
  • Ein Dot, der entlang der Linie „fährt"

Architektur (vereinfacht)

Kontinuierliches Mapping von Scroll- und DOM-Daten auf SVG-Ausgabe

Der entscheidende Punkt: Es gibt keine Events, keine Thresholds – nur ein kontinuierliches Mapping.

Warum kein IntersectionObserver?

IntersectionObserver beantwortet nur:

Ist Element X sichtbar, ja oder nein?

Aber nicht:

  • Wie weit ist es sichtbar?
  • Wo genau bin ich zwischen zwei Sections?

Das führt zu Flackern, ungenauen States und „sprunghafter" Navigation.

Ich wollte stattdessen: Pixel-genaue Kontrolle.

Die technische Umsetzung

1. SVG Path generieren

Aus den Heading-Positionen entsteht ein Path:

  • Vertikale Linie
  • Indent bei h3
  • Bezier-Kurven für saubere Übergänge

2. Viewport als Clip

Der sichtbare Bereich wird nicht berechnet, sondern geclippt.

  • Hintergrund-Track = gesamte Struktur
  • Vordergrund-Track = per clipPath auf Viewport begrenzt

Das fühlt sich deutlich natürlicher an als „aktive Items togglen".

3. Dot auf dem Path

Und hier kam der Part, bei dem ich wirklich gestruggelt habe.

Ich hatte am Anfang mehrere Ansätze im Kopf:

  • Prozentuale Mapping-Ansätze
  • Direkte Line-Interpolation
  • Scroll → Index Mapping

Alles hat sich irgendwie ungenau oder „off" angefühlt.

Ich habe ungelogen Stunden daran gesessen und iteriert, verworfen, neu gedacht.

Der finale Ansatz: Der Dot wird jedes Frame per requestAnimationFrame berechnet.

  • Binary Search entlang des SVG Paths
  • Position → transform: translate(...)

Kein React State → keine Re-Renders. Das läuft komplett außerhalb von React.

Das war der Punkt, wo es sich plötzlich richtig angefühlt hat.

4. Headless Core

Die gesamte Logik steckt in:

useTocNavigation()

Damit kannst du eigene UI, eigene Styles und eigene Interaction Patterns bauen.

Features im Überblick

  • Scroll-synchronisierte SVG Navigation
  • Viewport-basierter Highlight-Track
  • Dot, der entlang der Struktur fährt
  • Saubere Übergänge zwischen h2 und h3
  • Layout-Modi: rechts, links oder inline
  • Headless Hook für maximale Flexibilität
  • Keine externen Dependencies

Beispiel

import { TocNav } from 'toc-nav'
 
<TocNav items={headings} containerWidth="56rem" />

Das rendert:

  • Mobile: collapsible ToC
  • Desktop: fixe Sidebar
  • Automatisch versteckt bei zu wenigen Headings

Wann das wirklich sinnvoll ist

Das Ding ist nicht für jede Seite gedacht.

Aber perfekt für:

  • technische Dokumentation
  • lange Blogposts
  • MDX-basierte Seiten
  • alles, wo Struktur wichtig ist

Open Source

Eigentlich war das nur für meinen Blog gedacht.

Aber ganz ehrlich: Es wäre Verschwendung gewesen, das nicht zu teilen.

@atlas-kit/toc-nav auf npm

@junet-1/toc-nav auf GitHub


Und das fühlt sich komplett anders an.

Einfach sauber.

Teilen: