{
 "$schema": "../../schema.json",
 "schema": "fundus/1",
 "id": "scrollspy-kapitelnavigation",
 "name": "Scrollspy-Kapitelnavigation (Sticky-Kapitelindex)",
 "name_en": "Scrollspy / section progress navigation / Sticky chapter index / table of contents",
 "kurz": "Ein Kapitelindex klebt neben dem Text, markiert das Kapitel in der Bildmitte und füllt je Kapitel eine Haarlinie, scrollgekoppelt und ohne JavaScript.",
 "kategorie": "Navigation",
 "kategorie_slug": "navigation",
 "kategorie_nr": 3,
 "bau_chat": "C3",
 "prioritaet": "B",
 "trendstatus": "aufsteigend",
 "belegstatus": "belegt",
 "website_belege": 2,
 "stimmung": [
  "ruhig",
  "editorial",
  "präzise"
 ],
 "stimmung_roh": [
  "sachlich",
  "editorial",
  "strukturiert",
  "ruhig"
 ],
 "technik": [
  "css",
  "scroll-driven-css",
  "vanilla-js",
  "intersection-observer",
  "position-sticky",
  "scroll-state-queries"
 ],
 "technik_gruppen": [
  "css",
  "js"
 ],
 "leistungskosten": "niedrig",
 "steckbrief": {
  "beschreibung": "1) Eine Kapitelnavigation listet die Abschnitte einer langen Seite: als Seitenspalte, als Inhaltsverzeichnis oder als Dock unten. 2) Der Eintrag des Kapitels, das gerade in der Bildmitte steht, wird hervorgehoben (Farbe, Indikator), oft mit einem Fortschrittsbalken je Kapitel. 3) Ein Klick springt weich zum Kapitel. 4) Seit 2026 geht das nativ: scroll-target-group: auto am Nav-Container und :target-current für den aktiven Link, ohne IntersectionObserver. Den Fortschritt je Kapitel liefern benannte view-timelines, ebenfalls ohne JavaScript.",
  "warum_es_wirkt": "Lange Seiten werden überschaubar. Man sieht jederzeit, wo man steht, wie viel noch kommt und wohin man springen kann. Die Gliederung wird selbst zum Gestaltungselement.",
  "wann_einsetzen": "Case Studies, Reports, Werkberichte, Dokumentation, lange Landingpages und Storytelling-Seiten mit klaren Kapiteln.",
  "wann_nicht": "Kurze Seiten mit zwei oder drei Abschnitten. Seiten mit waagerechtem Scroll-Hijacking ohne klare Abschnitte.",
  "varianten": [
   "Sticky-Seitenspalte mit nummerierten Kapiteln (01 Aufgabe, 02 Konzept …) und Indikatorlinie je Eintrag",
   "Section Anchor Dock unten (Osmo)",
   "One-page Progress Navigation mit Balken (Osmo)",
   "Sidebar mit Scroll-Fortschrittsanzeige (Raw Materials laut Navbar Gallery)",
   "Waagerechte Kapitelleiste oben, auf dem Telefon mit aufklappbarer Liste",
   "Fortschrittsbalken je Kapitel statt einer Gesamtleiste",
   "Festgeklebte Kapitelüberschriften, die per scroll-state(stuck) ihre Darstellung wechseln"
  ],
  "umsetzung": "Kapitelliste als <nav aria-label=\"Kapitel\"> mit Ankerlinks. Aktiver Link nativ: nav { scroll-target-group: auto } und nav a:is(:target-current, .aktiv) { … }. Selektoren in :is() kombinieren, nicht per Komma, sonst kippt die ganze Regel in Browsern ohne :target-current. aria-current per JavaScript mitpflegen (beim Scrollen und bei scrollend). Fallback: IntersectionObserver mit rootMargin '-50% 0px -50% 0px' setzt die Klasse .aktiv. Fortschritt ohne JavaScript: timeline-scope: --k1, --k2 … auf einem gemeinsamen Elternteil von Navigation und Kapiteln, jedes Kapitel bekommt view-timeline: --kN block, die Spur des Eintrags animation: fuellen linear both; animation-timeline: --kN; animation-range: cover 50vh cover calc(100% - 50vh), animiert scale von 0 auf 1. Die Spalte klebt mit position: sticky; top: 0; height: 100svh. Am saubersten als Rasterzelle über alle Zeilen, dann endet sie mit dem letzten Kapitel. Der Trick mit margin-bottom: -100svh lässt sie dagegen über den Fuß hinauslaufen. scroll-margin-top auf den Kapiteln, wenn eine Leiste oben klebt. Stil-Wechsel festgeklebter Überschriften: container-type: scroll-state und @container scroll-state(stuck: top) hinter @supports. Abschnitte so hoch anlegen, dass höchstens zwei gleichzeitig sichtbar sind, und nach dem letzten Kapitel mindestens 50vh Platz lassen.",
  "mobil": "Seitenspalte ausblenden. Stattdessen eine schmale Leiste oben mit dem aktuellen Kapitelnamen und einem Fortschritt je Kapitel; Tippen klappt die Liste auf. Alternativ ein Dock unten mit Kapitelname und Ziffern. Trefferflächen mindestens 44 px.",
  "reduzierte_bewegung": "scroll-behavior: smooth nur bei prefers-reduced-motion: no-preference, sonst springt der Link direkt. Die scrollgekoppelte Füllung ist unkritisch, weil sie nur dem eigenen Scrollen folgt. Wer sie abschaltet, zeigt den Zustand statt der Bewegung: erledigte Kapitel voll, kommende leer.",
  "barrierefreiheit": ":target-current wird Screenreadern nicht gemeldet, deshalb aria-current=\"true\" am aktiven Link mitpflegen. Den Zustand nicht nur über Farbe zeigen (Gewicht, Linie, Ziffer). Nach dem Sprung den Fokus auf die Überschrift setzen (tabindex=\"-1\", focus mit preventScroll). Die aufklappbare Telefon-Liste braucht aria-expanded und aria-controls und schließt mit Escape."
 },
 "messwerte": {
  "easing": "linear (scrollgekoppelt)",
  "dauer_ms": "scrollgekoppelt",
  "stagger_ms": "",
  "distanz": "Indikator scaleY 0 → 1",
  "sonstiges": "timeline-scope + benannte view-timelines je Überschrift, kein JavaScript",
  "herkunft": "beobachtet",
  "quelle": "https://nerdy.dev/style.css?__frsh_c=1xfeax0m04df",
  "aus_eintrag": [
   "scrollspy-fortschritts-navigation"
  ]
 },
 "kurve": {
  "bezier": [
   0,
   0,
   1,
   1
  ],
  "css": "cubic-bezier(0, 0, 1, 1)",
  "name": "linear",
  "dauer_ms": null
 },
 "gesehen_bei": [
  {
   "seite": "Chrome Developers: Build a Scrollspy Navigation",
   "url": "https://developer.chrome.com/docs/modern-web-guidance/guides/ui-components/scrollspy",
   "auszeichnung": "",
   "beobachtung": "scroll-target-group: auto, :target-current, aria-current-Sync bei scrollend, IntersectionObserver-Fallback mit rootMargin -50 % 0px -50 % 0px.",
   "art": "artikel"
  },
  {
   "seite": "Osmo: One-page Progress Navigation",
   "url": "https://www.osmo.supply/resource/onepage-progress-navigation",
   "auszeichnung": "Osmo-Ressource",
   "beobachtung": "Nur Titel gesehen.",
   "art": "demo"
  },
  {
   "seite": "Osmo: Section Anchor Dock",
   "url": "https://www.osmo.supply/resource/section-anchor-dock",
   "auszeichnung": "Osmo-Ressource",
   "beobachtung": "Nur Titel gesehen.",
   "art": "demo"
  },
  {
   "seite": "Navbar Gallery: Sidebar-Beispiele 2026",
   "url": "https://www.navbar.gallery/blog/best-side-bar-navigation-menu-design-examples",
   "auszeichnung": "",
   "beobachtung": "Raw Materials: animierte Sidebar zeigt Scroll-Fortschritt.",
   "art": "artikel"
  },
  {
   "seite": "Chrome Developers – scroll-state queries",
   "url": "https://developer.chrome.com/blog/css-scroll-state-queries?hl=de",
   "auszeichnung": "",
   "beobachtung": "Neue CSS-Grundlage (Chrome 133) für Stil-Wechsel festgeklebter Elemente: @container scroll-state(stuck: top).",
   "art": "artikel"
  },
  {
   "seite": "nerdy.dev (Adam Argyle)",
   "url": "https://nerdy.dev/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft (style.css): reines CSS – .BlogDetail timeline-scope:--h2-1 … --h2-15, jede h2 view-timeline:--h2-n, der Inhaltsverzeichnis-Eintrag animiert :before scaleY(0 → 1) über animation-timeline:--h2-n (animation scroll-spy linear both); reduced-motion zeigt scaleY(1).",
   "art": "website"
  },
  {
   "seite": "Stripe",
   "url": "https://stripe.com/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft (d0587afad5d01631.css): Seitennavigation des Design-Systems (.hds-side-nav) mit Indikator, der per anchor-name:--active-nav dem [aria-current]-Link folgt (aktiver Abschnitt).",
   "art": "website"
  }
 ],
 "tutorials": [
  {
   "titel": "Chrome Developers: Build a Scrollspy Navigation",
   "url": "https://developer.chrome.com/docs/modern-web-guidance/guides/ui-components/scrollspy"
  },
  {
   "titel": "modern-css.com: CSS Scroll Spy with :target-current",
   "url": "https://modern-css.com/scroll-spy-without-intersection-observer/"
  },
  {
   "titel": "CSS scroll-state queries (Chrome Developers)",
   "url": "https://developer.chrome.com/blog/css-scroll-state-queries?hl=de"
  },
  {
   "titel": "A Practical Introduction to Scroll-Driven Animations (Codrops)",
   "url": "https://tympanus.net/codrops/2024/01/17/a-practical-introduction-to-scroll-driven-animations-with-css-scroll-and-view/"
  }
 ],
 "kombinierbar_mit": [
  "gleitender-indikator",
  "seitenrand-und-ecken-navigation",
  "mobile-bottom-navigation",
  "case-study-geruest",
  "editorial-magazin-layout",
  "split-screen-sticky-seiten",
  "scroll-fortschritt"
 ],
 "urls": {
  "detail": "https://cecodesign.de/lab/fundus/navigation/scrollspy-kapitelnavigation/",
  "meta": "https://cecodesign.de/lab/fundus/navigation/scrollspy-kapitelnavigation/meta.json",
  "demo": "https://cecodesign.de/lab/fundus/navigation/scrollspy-kapitelnavigation/demo"
 },
 "herkunft": {
  "quelldateien": [
   "navigation.json",
   "layouts.json",
   "patch-belege-3.json"
  ],
  "pruefnotizen": [
   "patch-belege-3.json: CSS-only-Scrollspy als Produktionsbeleg."
  ],
  "zusammengelegt_aus": [
   "scrollspy-fortschritts-navigation",
   "kapitel-index-sticky"
  ],
  "querverweis_kategorien": [
   "Hero & Section-Layouts"
  ]
 },
 "bau": {
  "status": "fertig",
  "kurz": "Ein Kapitelindex klebt neben dem Text, markiert das Kapitel in der Bildmitte und füllt je Kapitel eine Haarlinie, scrollgekoppelt und ohne JavaScript.",
  "demo": true,
  "abhaengigkeiten": [],
  "werte": {
   "easing": "linear (scrollgekoppelt, Scroll ist die Zeit)",
   "fuellung": "Seitenspalte: scale 1 0 → 1 1, transform-origin top (scaleY). Dock und Telefon-Leiste: scale 0 1 → 1 1, transform-origin left (scaleX). animation: spur-y linear both",
   "zeitachsen": "timeline-scope: --k1 … --k6 auf dem gemeinsamen Elternteil .bericht; je Kapitel view-timeline: --kN block; Spur N: animation-timeline: --kN",
   "bereich": "animation-range: cover 50vh cover calc(100% - 50vh) (Kapitelanfang in Bildmitte bis Kapitelende in Bildmitte)",
   "aktiv": "nav { scroll-target-group: auto } und a:is(:target-current, .aktiv). Chrome schaltet um, wenn der Kapitelanfang die Bildmitte erreicht (gemessen bei 47–50 % Höhe)",
   "fallback": "IntersectionObserver rootMargin '-50% 0px -50% 0px' setzt .aktiv; ohne animation-timeline rechnet JS --p = (Bildmitte − Kapitel-top) / Kapitelhöhe",
   "aria": "aria-current=\"true\" beim Scrollen (rAF), 120 ms danach und bei scrollend nachgezogen",
   "sprung": "scrollIntoView smooth (reduziert: instant), danach Fokus auf h2 tabindex=-1 mit preventScroll",
   "farbe": "Ton der Navigation folgt der Fläche darunter, je Eintrag einzeln geprüft; Dock nimmt den Gegenton. Ohne Übergang (folgt dem Scroll Bild für Bild)",
   "menue_telefon": "öffnen 300 ms cubic-bezier(0.1, 0.9, 0.3, 1), schließen 200 ms cubic-bezier(0.4, 0, 0.2, 1), opacity + translate 0 −8px",
   "ui": "Knöpfe 150 ms cubic-bezier(0.1, 0.6, 0.4, 1)",
   "masse": "Spalte clamp(200px, 18vw, 260px); Spur 1 px, Abstand 4 px, Eintrag min. 40 px (Touch 48 px); Telefon-Leiste 52 px mit sechs Segmenten à 2 px, Lücke 3 px; Dock max. 980 px, 20 px über dem Rand; Umbruch bei 879 px",
   "kapitel": "6 Kapitel, min-height 118svh (Desktop); Fuß 62svh, damit das letzte Kapitel voll wird",
   "schrift": "Bodoni Moda 400 (opsz auto) für Titel und Kapitelnamen, Instrument Sans 0.75rem versal +0.08em für Labels, Ziffern tabular-nums",
   "farben": "#23282f Anthrazit, #ffffff, #f4f4f2 Produktfläche, #676c73 Meta, #d9dadb Linien, #a88a5a Messing nur für aktive Spur, Ziffer und Fokus (Ziffer auf Hell als Text #8a6d40)"
  },
  "ausloeser": "scroll",
  "touch": "Unter 880 px wird die Seitenspalte zur 52 px hohen Leiste oben: aktuelles Kapitel als Knopf, darunter sechs Fortschrittssegmente. Tippen klappt die Kapitelliste auf (300 ms auf, 200 ms zu, Escape und Tippen außerhalb schließen). Das Dock zeigt oben den Kapitelnamen und darunter sechs Ziffern mit 48 px Trefferfläche.",
  "reduziert": "Kein weiches Scrollen, kein Selbstscroll in der Vorführung (Sprung Kapitel für Kapitel). Die Spuren laufen nicht mit: erledigte Kapitel und das aktive zeigen die volle Linie, kommende eine leere.",
  "korrekturen": {
   "beschreibung": "1) Eine Kapitelnavigation listet die Abschnitte einer langen Seite: als Seitenspalte, als Inhaltsverzeichnis oder als Dock unten. 2) Der Eintrag des Kapitels, das gerade in der Bildmitte steht, wird hervorgehoben (Farbe, Indikator), oft mit einem Fortschrittsbalken je Kapitel. 3) Ein Klick springt weich zum Kapitel. 4) Seit 2026 geht das nativ: scroll-target-group: auto am Nav-Container und :target-current für den aktiven Link, ohne IntersectionObserver. Den Fortschritt je Kapitel liefern benannte view-timelines, ebenfalls ohne JavaScript.",
   "warum_es_wirkt": "Lange Seiten werden überschaubar. Man sieht jederzeit, wo man steht, wie viel noch kommt und wohin man springen kann. Die Gliederung wird selbst zum Gestaltungselement.",
   "wann_einsetzen": "Case Studies, Reports, Werkberichte, Dokumentation, lange Landingpages und Storytelling-Seiten mit klaren Kapiteln.",
   "wann_nicht": "Kurze Seiten mit zwei oder drei Abschnitten. Seiten mit waagerechtem Scroll-Hijacking ohne klare Abschnitte.",
   "varianten": [
    "Sticky-Seitenspalte mit nummerierten Kapiteln (01 Aufgabe, 02 Konzept …) und Indikatorlinie je Eintrag",
    "Section Anchor Dock unten (Osmo)",
    "One-page Progress Navigation mit Balken (Osmo)",
    "Sidebar mit Scroll-Fortschrittsanzeige (Raw Materials laut Navbar Gallery)",
    "Waagerechte Kapitelleiste oben, auf dem Telefon mit aufklappbarer Liste",
    "Fortschrittsbalken je Kapitel statt einer Gesamtleiste",
    "Festgeklebte Kapitelüberschriften, die per scroll-state(stuck) ihre Darstellung wechseln"
   ],
   "umsetzung": "Kapitelliste als <nav aria-label=\"Kapitel\"> mit Ankerlinks. Aktiver Link nativ: nav { scroll-target-group: auto } und nav a:is(:target-current, .aktiv) { … }. Selektoren in :is() kombinieren, nicht per Komma, sonst kippt die ganze Regel in Browsern ohne :target-current. aria-current per JavaScript mitpflegen (beim Scrollen und bei scrollend). Fallback: IntersectionObserver mit rootMargin '-50% 0px -50% 0px' setzt die Klasse .aktiv. Fortschritt ohne JavaScript: timeline-scope: --k1, --k2 … auf einem gemeinsamen Elternteil von Navigation und Kapiteln, jedes Kapitel bekommt view-timeline: --kN block, die Spur des Eintrags animation: fuellen linear both; animation-timeline: --kN; animation-range: cover 50vh cover calc(100% - 50vh), animiert scale von 0 auf 1. Die Spalte klebt mit position: sticky; top: 0; height: 100svh. Am saubersten als Rasterzelle über alle Zeilen, dann endet sie mit dem letzten Kapitel. Der Trick mit margin-bottom: -100svh lässt sie dagegen über den Fuß hinauslaufen. scroll-margin-top auf den Kapiteln, wenn eine Leiste oben klebt. Stil-Wechsel festgeklebter Überschriften: container-type: scroll-state und @container scroll-state(stuck: top) hinter @supports. Abschnitte so hoch anlegen, dass höchstens zwei gleichzeitig sichtbar sind, und nach dem letzten Kapitel mindestens 50vh Platz lassen.",
   "mobil": "Seitenspalte ausblenden. Stattdessen eine schmale Leiste oben mit dem aktuellen Kapitelnamen und einem Fortschritt je Kapitel; Tippen klappt die Liste auf. Alternativ ein Dock unten mit Kapitelname und Ziffern. Trefferflächen mindestens 44 px.",
   "reduzierte_bewegung": "scroll-behavior: smooth nur bei prefers-reduced-motion: no-preference, sonst springt der Link direkt. Die scrollgekoppelte Füllung ist unkritisch, weil sie nur dem eigenen Scrollen folgt. Wer sie abschaltet, zeigt den Zustand statt der Bewegung: erledigte Kapitel voll, kommende leer.",
   "barrierefreiheit": ":target-current wird Screenreadern nicht gemeldet, deshalb aria-current=\"true\" am aktiven Link mitpflegen. Den Zustand nicht nur über Farbe zeigen (Gewicht, Linie, Ziffer). Nach dem Sprung den Fokus auf die Überschrift setzen (tabindex=\"-1\", focus mit preventScroll). Die aufklappbare Telefon-Liste braucht aria-expanded und aria-controls und schließt mit Escape."
  },
  "geprueft": "01.11.2026",
  "notiz": "Werkbericht der erfundenen Uhrmacherei Kaulberg aus Bamberg (Rezept luxus-manufaktur-stille: Anthrazit und Weiß im Wechsel, Bodoni Moda, Messing nur als Linie). Sechs Kapitel mit eigenen SVG-Zeichnungen (Uhr, Kaliber, Emaille-Ausschnitt, Gehäuseprofil, Prüfkurve). Umschalter Seitenspalte und Dock unten, Telefon mit Leiste oben und aufklappbarer Liste. Aktiver Eintrag nativ über :target-current, Füllung rein in CSS über timeline-scope und benannte view-timelines; beides mit JS-Fallback, die eingeblendeten Werte zeigen, welcher Weg läuft. Die Navigation übernimmt den Ton der Fläche darunter, je Eintrag einzeln. Vorführung: liest sich selbst durch, springt per Klick zurück zu Kapitel 02 und wechselt jede Runde die Variante. AD-Review 01.11.2026: Note 4. Zugang 02.11.2026: Meta/--leise auf Hell #8b9097 → #676c73 (5,3:1, 4,8:1 auf #f4f4f2); Messing als Textfarbe auf Hell #8a6d40 (Token --messing-text, auf Dunkel bleibt #a88a5a); Ton der Navigationsteile nach der Mitte ihres sichtbaren Teils und nach dem Laden der Schriften neu bestimmt (der Stand unten links war bei 800 px Höhe weiß auf weiß); Kontrastmodus-Rahmen für Knöpfe. Offen (Fehlalarm): „Fokus außerhalb des Viewports“ entsteht durch scroll-behavior: smooth, das fokussierte Element ist nach dem Scrollen sichtbar."
 }
}
