{
 "$schema": "../../schema.json",
 "schema": "fundus/1",
 "id": "css-scroll-triggered",
 "name": "CSS Scroll-Triggered Animations (zeitbasiert, ausgelöst durch Scroll)",
 "name_en": "CSS scroll-triggered animations (timeline-trigger / animation-trigger)",
 "kurz": "Zeitbasierte CSS-Animationen, die der Scroll nur auslöst: Projektbilder und Texte eines Architekturbüros spielen ihre 350 oder 750 ms zu Ende, auch wenn der Scroll steht, und laufen beim Verlassen rückwärts.",
 "kategorie": "Scroll & Section-Animationen",
 "kategorie_slug": "scroll",
 "kategorie_nr": 5,
 "bau_chat": "C2",
 "prioritaet": "C",
 "trendstatus": "aufsteigend",
 "belegstatus": "quellenbelegt",
 "website_belege": 0,
 "stimmung": [
  "ruhig",
  "präzise"
 ],
 "stimmung_roh": [
  "technisch",
  "präzise",
  "leicht"
 ],
 "technik": [
  "css",
  "scroll-driven-css"
 ],
 "technik_gruppen": [
  "css"
 ],
 "leistungskosten": "niedrig",
 "steckbrief": {
  "beschreibung": "Eine normale, zeitbasierte CSS-Animation (etwa 350 ms) startet nicht beim Laden, sondern durch einen Scroll-Trigger. timeline-trigger legt Name, Quelle (view() oder scroll()) und Aktivierungsbereich fest, nach einem Schrägstrich optional einen größeren Aktiv-Bereich. animation-trigger verbindet Animation und Trigger mit Aktionen, zum Beispiel play-forwards beim Eintritt und play-backwards beim Verlassen. Anders als bei scroll-driven Animationen läuft die Animation nach dem Auslösen in ihrer Dauer zu Ende, auch wenn der Scroll stoppt.",
  "warum_es_wirkt": "Sorgfältig getimte Reveals mit eigener Kurve und Dauer, aber nativ im Browser und ohne IntersectionObserver-JS.",
  "wann_einsetzen": "Einmalige oder umkehrbare Einblendungen von Karten, Überschriften und Bildern beim Scrollen, auch Akkordeon-artige Karten.",
  "wann_nicht": "Wenn die Animation exakt am Scroll kleben soll (dann scroll-driven). Und nicht als einzige Lösung, solange nur Chromium ab 145 es kann: ein Rückfall ist nötig.",
  "varianten": [
   "play-forwards und play-backwards (umkehrbar)",
   "Getrennte Aktivierungs- und Aktiv-Bereiche (contain, cover)",
   "Trigger auf der Sektion, Animation auf den Kindern",
   "trigger-scope bei vielen gleichen Elementen",
   "interpolate-size: allow-keywords, um die Höhe auf auto zu animieren"
  ],
  "umsetzung": "Beispiel aus dem Chrome-Blog: .card { timeline-trigger: --t view() contain 15% contain 85% / entry 100% exit 0%; trigger-scope: --t; animation: unclip .35s ease-in-out both; animation-trigger: --t play-forwards play-backwards } .card > * { interpolate-size: allow-keywords; animation: card-contents .35s ease-in-out both; animation-trigger: --t play-forwards play-backwards }. Ohne Bereich gilt cover. Trigger-Namen sind global, die letzte Deklaration gewinnt, deshalb trigger-scope setzen. Erkennung über @supports (timeline-trigger-name: --x) oder CSS.supports(); sonst ein IntersectionObserver, der eine Klasse setzt. Für bildhohe Elemente ist contain 15 % bis 85 % sehr schmal; dort einen cover-Bereich wählen. Weitere Aktionen stehen in der Spezifikation, der Artikel zeigt nur play-forwards und play-backwards.",
  "mobil": "Unproblematisch.",
  "reduzierte_bewegung": "animation: none unter prefers-reduced-motion: reduce, oder nur Deckkraft.",
  "barrierefreiheit": "Ohne Unterstützung oder ohne Auslösung muss der sichtbare Endzustand gelten. fill-mode und Startzustand prüfen: Die versteckten Startwerte gehören nur in die @supports-Regel."
 },
 "messwerte": {
  "easing": "ease-in-out (Beispiel)",
  "dauer_ms": "350",
  "stagger_ms": "",
  "distanz": "",
  "sonstiges": "Aktivierung contain 15%–85%, aktiv entry 100%–exit 0%; Chrome 145",
  "herkunft": "beobachtet",
  "quelle": "https://developer.chrome.com/blog/scroll-triggered-animations"
 },
 "kurve": {
  "bezier": [
   0.42,
   0,
   0.58,
   1
  ],
  "css": "cubic-bezier(0.42, 0, 0.58, 1)",
  "name": "ease-in-out",
  "dauer_ms": 350
 },
 "gesehen_bei": [
  {
   "seite": "Chrome Developers Blog – Scroll-Triggered Animations",
   "url": "https://developer.chrome.com/blog/scroll-triggered-animations",
   "auszeichnung": "",
   "beobachtung": "Kartenbeispiel: unclip 0.35s ease-in-out, timeline-trigger: --t view() contain 15% contain 85% / entry 100% exit 0%, Fallback via IntersectionObserver.",
   "art": "artikel"
  },
  {
   "seite": "Bramus – Scroll-Triggered Animations Debugger (14.10.2026)",
   "url": "https://www.bram.us/2026/10/14/scroll-triggered-animations-debugger-devtools-extension/",
   "auszeichnung": "",
   "beobachtung": "DevTools-Extension visualisiert timeline-trigger-activation-range (blau) und active-range (grün).",
   "art": "artikel"
  },
  {
   "seite": "CodePen – Meet the monsters (Scrollytelling, Bramus)",
   "url": "https://codepen.io/bramus/pen/ZYWPRbr",
   "auszeichnung": "",
   "beobachtung": "Scrollytelling-Demo mit Triggern (aus Chrome-Blog verlinkt).",
   "art": "demo"
  }
 ],
 "tutorials": [
  {
   "titel": "CSS scroll-triggered animations (Chrome)",
   "url": "https://developer.chrome.com/blog/scroll-triggered-animations"
  },
  {
   "titel": "Timeline Trigger Ranges Visualizer (CodePen)",
   "url": "https://codepen.io/web-dot-dev/pen/wBGOYEq"
  },
  {
   "titel": "Trigger-Ranges-Visualizer (Bramus, CodePen)",
   "url": "https://codepen.io/bramus/pen/bNpzXrV"
  }
 ],
 "kombinierbar_mit": [
  "css-view-reveal-nativ",
  "split-mask-reveal",
  "clip-path-bild-reveal"
 ],
 "urls": {
  "detail": "https://cecodesign.de/lab/fundus/scroll/css-scroll-triggered/",
  "meta": "https://cecodesign.de/lab/fundus/scroll/css-scroll-triggered/meta.json",
  "demo": "https://cecodesign.de/lab/fundus/scroll/css-scroll-triggered/demo"
 },
 "herkunft": {
  "quelldateien": [
   "scroll.json",
   "patch-belege-3.json"
  ],
  "pruefnotizen": [
   "patch-belege-3.json: Auf keiner der rund 80 geprüften Seiten timeline-trigger gefunden (Chrome 145, sehr neu); Vorschlag C bis zur Verbreitung."
  ],
  "prioritaet_hinweis": "31.10.2026: B → C laut patch-belege-3.json (Auf keiner der rund 80 geprüften Seiten timeline-trigger gefunden (Chrome 145, sehr neu); Vorschlag C bis zur Verbreitung.)"
 },
 "bau": {
  "status": "fertig",
  "kurz": "Zeitbasierte CSS-Animationen, die der Scroll nur auslöst: Projektbilder und Texte eines Architekturbüros spielen ihre 350 oder 750 ms zu Ende, auch wenn der Scroll steht, und laufen beim Verlassen rückwärts.",
  "demo": true,
  "abhaengigkeiten": [],
  "werte": {
   "easing": "ease-in-out = cubic-bezier(0.42, 0, 0.58, 1) für alle ausgelösten Animationen (Messwert aus dem Chrome-Beispiel)",
   "dauer_text_ms": 350,
   "dauer_bild_ms": 750,
   "text": "@keyframes rein { from { opacity: 0; translate: 0 1.5rem } }, fill both",
   "bild": "Deckfläche #e1e3e3 über dem Bild, @keyframes deckel-weg scale 1 1 → 1 0, transform-origin oben (nur Transform, Rezeptregel)",
   "trigger_text": "timeline-trigger: --text view() contain 15% contain 85% / entry 100% exit 0%",
   "trigger_bild": "timeline-trigger: --bild view() cover 25% cover 75% / cover 0% cover 100% (große Bilder: contain 15–85 % ist bei bildhohen Elementen zu schmal und wird bei schnellem Scroll übersprungen)",
   "aktionen": "animation-trigger: <name> play-forwards play-backwards",
   "scope": "trigger-scope: --bild, --text auf jedem Projekt (Variante Element), --p (Variante Sektion)",
   "sektion_stagger": "animation-delay: 200 ms + i × 80 ms für Nummer, Titel, Text, Daten",
   "eng": "gleiche Aktivierungsbereiche ohne Aktiv-Bereich nach dem Schrägstrich",
   "erkennung": "@supports (timeline-trigger-name: --x) bzw. CSS.supports('timeline-trigger-name: --x')",
   "rueckfall": "IntersectionObserver: .an bei rootMargin -15 % oben und unten und threshold 0.25, entfernt bei 0 % Sichtbarkeit (Variante Eng: unter 60 %); Übergänge statt Animationen mit denselben Dauern",
   "status_pult": "document.getAnimations() mit playState running und Fortschritt zwischen 0 und 1, alle 90 ms; Scroll gilt 140 ms nach dem letzten scroll-Ereignis als stehend",
   "vorfuehrung": "Schritte von 50 % vh in 650 ms mit cubic-bezier(0.1, 0.6, 0.4, 1) (Rezeptkurve), 1300 ms Pause; aufwärts 80 % vh in 800 ms; Varianten wechseln je Runde",
   "typo": "Hanken Grotesk 450, h1 clamp(1.953rem, 1.2rem + 2.4vw, 3.052rem) −0,02em, Text 1.0625rem/1.45 −0,012em, Meta 0.8rem, Hervorhebung kursiv",
   "farben": "#ffffff Grund, #e1e3e3 Fläche, #231f20 Text, #76767a Meta, #1f2323 Fuß, #0022ff Fokus und Status"
  },
  "ausloeser": "scroll",
  "touch": "Gleiches Verhalten wie am Desktop, Scroll ist die Geste. Projekte stehen einspaltig untereinander, Bild über Text. Die Steuerung ist eingeklappt.",
  "reduziert": "Keine Animation: Deckflächen entfallen, Texte stehen sofort im Endzustand. Der Trigger bleibt wirkungslos, die Seite liest sich wie ohne Effekt.",
  "korrekturen": {
   "beschreibung": "Eine normale, zeitbasierte CSS-Animation (etwa 350 ms) startet nicht beim Laden, sondern durch einen Scroll-Trigger. timeline-trigger legt Name, Quelle (view() oder scroll()) und Aktivierungsbereich fest, nach einem Schrägstrich optional einen größeren Aktiv-Bereich. animation-trigger verbindet Animation und Trigger mit Aktionen, zum Beispiel play-forwards beim Eintritt und play-backwards beim Verlassen. Anders als bei scroll-driven Animationen läuft die Animation nach dem Auslösen in ihrer Dauer zu Ende, auch wenn der Scroll stoppt.",
   "warum_es_wirkt": "Sorgfältig getimte Reveals mit eigener Kurve und Dauer, aber nativ im Browser und ohne IntersectionObserver-JS.",
   "wann_einsetzen": "Einmalige oder umkehrbare Einblendungen von Karten, Überschriften und Bildern beim Scrollen, auch Akkordeon-artige Karten.",
   "wann_nicht": "Wenn die Animation exakt am Scroll kleben soll (dann scroll-driven). Und nicht als einzige Lösung, solange nur Chromium ab 145 es kann: ein Rückfall ist nötig.",
   "varianten": [
    "play-forwards und play-backwards (umkehrbar)",
    "Getrennte Aktivierungs- und Aktiv-Bereiche (contain, cover)",
    "Trigger auf der Sektion, Animation auf den Kindern",
    "trigger-scope bei vielen gleichen Elementen",
    "interpolate-size: allow-keywords, um die Höhe auf auto zu animieren"
   ],
   "umsetzung": "Beispiel aus dem Chrome-Blog: .card { timeline-trigger: --t view() contain 15% contain 85% / entry 100% exit 0%; trigger-scope: --t; animation: unclip .35s ease-in-out both; animation-trigger: --t play-forwards play-backwards } .card > * { interpolate-size: allow-keywords; animation: card-contents .35s ease-in-out both; animation-trigger: --t play-forwards play-backwards }. Ohne Bereich gilt cover. Trigger-Namen sind global, die letzte Deklaration gewinnt, deshalb trigger-scope setzen. Erkennung über @supports (timeline-trigger-name: --x) oder CSS.supports(); sonst ein IntersectionObserver, der eine Klasse setzt. Für bildhohe Elemente ist contain 15 % bis 85 % sehr schmal; dort einen cover-Bereich wählen. Weitere Aktionen stehen in der Spezifikation, der Artikel zeigt nur play-forwards und play-backwards.",
   "mobil": "Unproblematisch.",
   "reduzierte_bewegung": "animation: none unter prefers-reduced-motion: reduce, oder nur Deckkraft.",
   "barrierefreiheit": "Ohne Unterstützung oder ohne Auslösung muss der sichtbare Endzustand gelten. fill-mode und Startzustand prüfen: Die versteckten Startwerte gehören nur in die @supports-Regel."
  },
  "geprueft": "01.11.2026",
  "notiz": "Welt: Rezept architektur-ruhiges-raster, erfundenes Büro Halbhöhe Architekten aus Stuttgart (Partner Miriam Gerstl, Jonas Aichele) mit fünf Projekten; Bilder als eigene Canvas-Szenen, dazu ein Schnitt als SVG. Geprüft nativ in Chromium 153. Die Pult-Zeile zeigt, ob gerade gescrollt wird und wie viele Animationen laufen: so sieht man, dass sie nach dem Scroll-Stopp weiterlaufen. Kurve ease-in-out aus dem gemessenen Chrome-Beispiel statt der Rezeptkurve; die Rezeptkurve steuert nur die Selbst-Scroll-Fahrt. Referenzen: developer.chrome.com, bram.us, davidchipperfield.com, kaanarchitecten.com, big.dk. AD-Review 01.11.2026: Note 4. Pult startet eingeklappt (data-fundus-pult=\"zu\"), weil es mit 420 × 170 px beim Scrollen Projekttitel, Daten und die Mailadresse im Fuß verdeckt."
 }
}
