{
 "$schema": "../../schema.json",
 "schema": "fundus/1",
 "id": "horizontal-scroll-section",
 "name": "Gepinnte Horizontal-Scroll-Section",
 "name_en": "Horizontal scroll section (pinned) / Pinned horizontal scroll section",
 "kurz": "Eine Section bleibt stehen, und ein breiter Projektstreifen zieht beim senkrechten Scrollen seitlich vorbei, die Bilder darin wandern leicht mit.",
 "kategorie": "Scroll & Section-Animationen",
 "kategorie_slug": "scroll",
 "kategorie_nr": 5,
 "bau_chat": "C2",
 "prioritaet": "A",
 "trendstatus": "etabliert",
 "belegstatus": "belegt",
 "website_belege": 2,
 "stimmung": [
  "editorial",
  "filmisch",
  "verspielt",
  "laut"
 ],
 "stimmung_roh": [
  "editorial",
  "galerie",
  "filmisch",
  "dynamisch",
  "galerieartig",
  "verspielt"
 ],
 "technik": [
  "gsap",
  "scrolltrigger",
  "scroll-driven-css",
  "css",
  "lenis"
 ],
 "technik_gruppen": [
  "css",
  "gsap",
  "lenis"
 ],
 "leistungskosten": "mittel",
 "steckbrief": {
  "beschreibung": "Eine Section wird gepinnt. Ein breiter Streifen darin, etwa Projektkarten oder eine Zeitachse, wandert beim senkrechten Scrollen nach links, bis sein rechtes Ende am rechten Rand steht. Die senkrechte Scrollstrecke entspricht der Überlänge des Streifens. Innere Animationen wie leicht mitwandernde Bilder oder einblendende Titel hängen an der waagerechten Position. Danach scrollt die Seite wieder senkrecht.",
  "warum_es_wirkt": "Der Richtungswechsel bricht die Gleichförmigkeit des senkrechten Scrollens und markiert einen besonderen Abschnitt. Es fühlt sich an wie das Durchblättern einer Galerie oder eines Filmstreifens. Viele gleichrangige Elemente bekommen nacheinander die volle Bühne, ohne Raster.",
  "wann_einsetzen": "Ausgewählte Projekte, Zeitachsen, Prozessschritte, Fotostrecken und Produktreihen mit 4 bis 8 Elementen.",
  "wann_nicht": "Bei mehr als etwa 10 Elementen, weil die Strecke zu lang wird. Bei Inhalten, die man vergleichen muss. Für Kerninformationen, die jeder sehen muss. Auf dem Telefon nicht als erzwungenes Pinning.",
  "varianten": [
   "GSAP: x-Tween auf dem Streifen mit Pin, innere Animationen per containerAnimation",
   "Nur CSS: sticky und view-timeline, translateX(calc(-100% + 100vw))",
   "Mit Snap auf jede Karte",
   "Mobil: native waagerechte Leiste mit scroll-snap statt Pin",
   "Galerie mit Bild-Parallaxe im Streifen",
   "Zeitachse mit Jahreszahlen",
   "Eigenständiger Horizontal-Bereich, etwa für Fotografie",
   "Ziehbare Galerie ohne Pin"
  ],
  "umsetzung": "GSAP: const tween = gsap.to(track, { x: () => -(track.scrollWidth - innerWidth), ease: 'none', scrollTrigger: { trigger: section, pin: true, scrub: true, end: () => '+=' + (track.scrollWidth - innerWidth), invalidateOnRefresh: true } }). Innere Elemente: scrollTrigger: { containerAnimation: tween, trigger: card, start: 'left right' }. Die Container-Animation muss ease: 'none' haben. Pin und Snap gehen innerhalb von containerAnimation nicht. CSS: äußere Hülle mit Höhe 100svh plus Überlänge und view-timeline-name: --pin; innen position: sticky; top: 0; height: 100svh; overflow: clip. Der Streifen läuft mit animation: move linear both; animation-timeline: --pin; animation-range: contain 0% contain 100% und @keyframes move { to { transform: translateX(calc(-100% + 100vw)) } }. Fallstrick: overflow: hidden auf einem Vorfahren bricht sticky, dort overflow: clip oder visible verwenden. Bei Größenänderung die Überlänge neu messen (invalidateOnRefresh).",
  "mobil": "Kein Pin. Stattdessen eine native waagerechte Leiste mit overflow-x: auto und scroll-snap-type: x mandatory. Seitliches Wischen ist auf Touch natürlicher als senkrechtes Scrollen, das seitlich wirkt. Alternativ die Elemente senkrecht stapeln.",
  "reduzierte_bewegung": "Kein Pin. Die Elemente als normales Raster oder als frei scrollbare Scroll-Snap-Leiste zeigen.",
  "barrierefreiheit": "Liste semantisch als ul/li auszeichnen. Bei einem GSAP-Pin scrollt der Browser nicht automatisch zu einer fokussierten Karte außerhalb des Bilds. Ein focusin-Handler muss die passende Scrollposition setzen (Pin-Start plus Versatz der Karte, mit Lenis über scrollTo). Den Fokus deutlich sichtbar machen. Bei der nativen Leiste helfen Pfeilknöpfe oder ein CSS-Carousel mit ::scroll-button (Chrome 135+)."
 },
 "messwerte": {
  "easing": "none (zwingend linear)",
  "dauer_ms": "scrollgekoppelt (scrub true)",
  "stagger_ms": "",
  "distanz": "x bis -(scrollWidth - clientWidth); Pin-Länge = Überlänge",
  "sonstiges": "innere Parallaxe 0 → 4rem per containerAnimation (Lando)",
  "herkunft": "beobachtet",
  "quelle": "https://www.hugeinc.com/_next/static/chunks/1rurir4_qk9rk.js",
  "aus_eintrag": [
   "horizontal-scroll-section"
  ]
 },
 "kurve": {
  "bezier": [
   0,
   0,
   1,
   1
  ],
  "css": "cubic-bezier(0, 0, 1, 1)",
  "name": "linear",
  "dauer_ms": null
 },
 "gesehen_bei": [
  {
   "seite": "scroll-driven-animations.style – Horizontal Scroll Section (Bramus)",
   "url": "https://scroll-driven-animations.style/demos/horizontal-section/css/",
   "auszeichnung": "",
   "beobachtung": "Referenzdemo: 500vh hohe Section, sticky Wrapper, 250vmax breiter Streifen, Keyframe translateX(calc(-100% + 100vw)) über animation-range: contain 0% contain 100%.",
   "art": "demo"
  },
  {
   "seite": "Olivier Larose – Horizontal Scroll / Horizontal Section",
   "url": "https://blog.olivierlarose.com/tutorials/horizontal-scroll",
   "auszeichnung": "",
   "beobachtung": "Tutorial-Umsetzung mit Framer Motion/Lenis (inspiriert von Award-Seiten).",
   "art": "artikel"
  },
  {
   "seite": "Gianluca Gradogna Portfolio ’25",
   "url": "https://tympanus.net/codrops/2025/01/30/case-study-gianluca-gradogna-portfolio-25/",
   "auszeichnung": "Awwwards SOTD 23.01.2025, Portfolio Honors 01/2025",
   "beobachtung": "Laut Codrops eigener Horizontal-Scroll-Bereich für Reisefotografie, Übergänge mit Position, Opacity und Clip-Masken.",
   "art": "artikel"
  },
  {
   "seite": "Huge",
   "url": "https://www.hugeinc.com/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft (Chunk 1rurir4_qk9rk.js): [data-horizontal-scroll-panel] x → -(scrollWidth - clientWidth), ease none, ScrollTrigger pin auf der Section, end \"+=\" Überlänge, scrub true; per data-horizontal-scroll-disable auf Mobil/Tablet abschaltbar.",
   "art": "website"
  },
  {
   "seite": "Lando Norris",
   "url": "https://landonorris.com/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft (HTML + lando-by-OFF+BRAND.js): section[data-horizontal-section] mit CSS-sticky .horizontal-pin-sticky und .horizontal-track; Section-Höhe = Trackbreite - Viewportbreite; Bilder im Track zusätzlich per containerAnimation um 0 → 4rem versetzt.",
   "art": "website",
   "browser_gemessen": true
  }
 ],
 "tutorials": [
  {
   "titel": "Horizontal Scroll Section (CSS + WAAPI)",
   "url": "https://scroll-driven-animations.style/demos/horizontal-section/css/"
  },
  {
   "titel": "Olivier Larose – Horizontal Section",
   "url": "https://blog.olivierlarose.com/tutorials/horizontal-section"
  },
  {
   "titel": "GSAP containerAnimation",
   "url": "https://gsap.com/docs/v3/Plugins/ScrollTrigger/"
  },
  {
   "titel": "Carousels with CSS (Chrome Developers)",
   "url": "https://developer.chrome.com/blog/carousels-with-css?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": [
  "mehrschicht-parallax",
  "clip-path-bild-reveal",
  "marquee-laufband",
  "case-study-geruest",
  "meta-labels-hud-typografie",
  "elastisches-raster-lag"
 ],
 "urls": {
  "detail": "https://cecodesign.de/lab/fundus/scroll/horizontal-scroll-section/",
  "meta": "https://cecodesign.de/lab/fundus/scroll/horizontal-scroll-section/meta.json",
  "demo": "https://cecodesign.de/lab/fundus/scroll/horizontal-scroll-section/demo"
 },
 "herkunft": {
  "quelldateien": [
   "scroll.json",
   "layouts.json",
   "patch-belege-3.json"
  ],
  "pruefnotizen": [
   "patch-belege-3.json: Zwei Produktionsbelege; Awwwards-Seiten entfernt."
  ],
  "zusammengelegt_aus": [
   "horizontal-scroll-section",
   "horizontal-scroll-sektion"
  ],
  "querverweis_kategorien": [
   "Hero & Section-Layouts"
  ]
 },
 "bau": {
  "status": "fertig",
  "kurz": "Eine Section bleibt stehen, und ein breiter Projektstreifen zieht beim senkrechten Scrollen seitlich vorbei, die Bilder darin wandern leicht mit.",
  "demo": true,
  "abhaengigkeiten": [
   "gsap@3.15.0",
   "ScrollTrigger@3.15.0"
  ],
  "werte": {
   "easing": "none (Container-Tween zwingend linear)",
   "scrub": "true",
   "distanz": "x: 0 → -(spur.scrollWidth - buehne.clientWidth), bei 1440 px Breite rund 3.556 px",
   "pin": "trigger: Section, start 'top top', end '+=' + Überlänge, pin: true, anticipatePin: 1, invalidateOnRefresh: true",
   "innere_parallaxe": "Bild x 0 → 4rem per containerAnimation, start 'left right', end 'right left', ease none, Bild 4rem breiter und um -4rem versetzt",
   "unterzeile": "opacity 0 → 1, y 24px → 0 per containerAnimation, start 'left 96%', end 'left 66%', ease none",
   "snap_variante": "snapTo: Kartenmitten als Fortschritt, duration { min: 0.3, max: 0.8 }, delay 0.08, ease expo.out",
   "css_variante": "Hülle height: calc(100svh + Überlänge), view-timeline-name: --pin; Bühne position: sticky, top 0, overflow: clip, container-type: inline-size; Spur animation-range: contain 0% contain 100%, @keyframes translateX(calc(-100% + 100cqw)); Bild translateX 0 → 4rem auf derselben Timeline",
   "spur": "Abstand clamp(28px, 4.2vw, 76px); Hochformat 4:5 min(56svh, 600px) hoch, Querformat 3:2 min(42svh, 440px) hoch, versetzt oben/unten",
   "leiste_mobil": "overflow-x: auto; scroll-snap-type: x mandatory; scroll-snap-align: start; overscroll-behavior-x: contain",
   "hover": "Bild scale 1 → 1.03, 1,2 s cubic-bezier(0.16, 1, 0.3, 1)",
   "vorfuehrung": "Selbst-Scroll 11 s pro Runde: 0,66 s halten, 7,3 s vor (Kosinus), 0,9 s halten, 2,2 s zurück"
  },
  "ausloeser": "scroll",
  "touch": "Unter 760 px Breite oder bei grobem Zeiger ohne Hover kein Pin: die Spur wird eine native Leiste mit overflow-x und scroll-snap (x mandatory), dazu Pfeilknöpfe und Zähler. Gewischt wird seitlich.",
  "reduziert": "Kein Pin, keine Parallaxe, kein Hover-Zoom: dieselbe frei scrollbare Leiste mit scroll-snap wie auf dem Telefon, Pfeilknöpfe springen ohne Glätten.",
  "korrekturen": {
   "beschreibung": "Eine Section wird gepinnt. Ein breiter Streifen darin, etwa Projektkarten oder eine Zeitachse, wandert beim senkrechten Scrollen nach links, bis sein rechtes Ende am rechten Rand steht. Die senkrechte Scrollstrecke entspricht der Überlänge des Streifens. Innere Animationen wie leicht mitwandernde Bilder oder einblendende Titel hängen an der waagerechten Position. Danach scrollt die Seite wieder senkrecht.",
   "warum_es_wirkt": "Der Richtungswechsel bricht die Gleichförmigkeit des senkrechten Scrollens und markiert einen besonderen Abschnitt. Es fühlt sich an wie das Durchblättern einer Galerie oder eines Filmstreifens. Viele gleichrangige Elemente bekommen nacheinander die volle Bühne, ohne Raster.",
   "wann_einsetzen": "Ausgewählte Projekte, Zeitachsen, Prozessschritte, Fotostrecken und Produktreihen mit 4 bis 8 Elementen.",
   "wann_nicht": "Bei mehr als etwa 10 Elementen, weil die Strecke zu lang wird. Bei Inhalten, die man vergleichen muss. Für Kerninformationen, die jeder sehen muss. Auf dem Telefon nicht als erzwungenes Pinning.",
   "varianten": [
    "GSAP: x-Tween auf dem Streifen mit Pin, innere Animationen per containerAnimation",
    "Nur CSS: sticky und view-timeline, translateX(calc(-100% + 100vw))",
    "Mit Snap auf jede Karte",
    "Mobil: native waagerechte Leiste mit scroll-snap statt Pin",
    "Galerie mit Bild-Parallaxe im Streifen",
    "Zeitachse mit Jahreszahlen",
    "Eigenständiger Horizontal-Bereich, etwa für Fotografie",
    "Ziehbare Galerie ohne Pin"
   ],
   "umsetzung": "GSAP: const tween = gsap.to(track, { x: () => -(track.scrollWidth - innerWidth), ease: 'none', scrollTrigger: { trigger: section, pin: true, scrub: true, end: () => '+=' + (track.scrollWidth - innerWidth), invalidateOnRefresh: true } }). Innere Elemente: scrollTrigger: { containerAnimation: tween, trigger: card, start: 'left right' }. Die Container-Animation muss ease: 'none' haben. Pin und Snap gehen innerhalb von containerAnimation nicht. CSS: äußere Hülle mit Höhe 100svh plus Überlänge und view-timeline-name: --pin; innen position: sticky; top: 0; height: 100svh; overflow: clip. Der Streifen läuft mit animation: move linear both; animation-timeline: --pin; animation-range: contain 0% contain 100% und @keyframes move { to { transform: translateX(calc(-100% + 100vw)) } }. Fallstrick: overflow: hidden auf einem Vorfahren bricht sticky, dort overflow: clip oder visible verwenden. Bei Größenänderung die Überlänge neu messen (invalidateOnRefresh).",
   "mobil": "Kein Pin. Stattdessen eine native waagerechte Leiste mit overflow-x: auto und scroll-snap-type: x mandatory. Seitliches Wischen ist auf Touch natürlicher als senkrechtes Scrollen, das seitlich wirkt. Alternativ die Elemente senkrecht stapeln.",
   "reduzierte_bewegung": "Kein Pin. Die Elemente als normales Raster oder als frei scrollbare Scroll-Snap-Leiste zeigen.",
   "barrierefreiheit": "Liste semantisch als ul/li auszeichnen. Bei einem GSAP-Pin scrollt der Browser nicht automatisch zu einer fokussierten Karte außerhalb des Bilds. Ein focusin-Handler muss die passende Scrollposition setzen (Pin-Start plus Versatz der Karte, mit Lenis über scrollTo). Den Fokus deutlich sichtbar machen. Bei der nativen Leiste helfen Pfeilknöpfe oder ein CSS-Carousel mit ::scroll-button (Chrome 135+)."
  },
  "geprueft": "31.10.2026",
  "notiz": "Werkschau eines erfundenen Markenstudios (Studio Kante, Köln): Einleitung, sieben Karten im Wechsel hoch/quer und ein Schlussfeld laufen gepinnt seitlich durch, danach geht die Seite senkrecht weiter. Umschalter: GSAP mit Pin (scrub true wie bei Huge), Mit Snap (Kartenmitte, expo.out) und Nur CSS (sticky + view-timeline, Überlänge per JS als --ueberlaenge gemessen). Bilder wandern per containerAnimation 0 → 4rem (Lando). Fortschrittslinie, Zähler und Werte unten. Tastaturfokus zieht die Scrollposition nach. Telefon und reduzierte Bewegung: native Scroll-Snap-Leiste. Vorschau scrollt selbst durch die Strecke und zurück. Bilder 01.11.2026: eigene Bilder statt Portfolio. Zugang 02.11.2026: --leise Alpha .56 auf .64 (4,8:1), Knopfrahmen im Kontrastmodus. Offen (Effekt): Bildunterschrift der Karte am rechten Rand blendet per Scrub ein und ist im Ruhezustand halb transparent."
 }
}
