{
 "$schema": "../../schema.json",
 "schema": "fundus/1",
 "id": "stapelkarten",
 "name": "Stapelkarten beim Scrollen (Stacking Cards)",
 "name_en": "Stacking cards (sticky card stack) / Sticky stacking cards",
 "kurz": "Projektkarten docken beim Scrollen nacheinander an und legen sich übereinander; die verdeckten werden kleiner und dunkler, kippen weg oder bleiben als Register stehen.",
 "kategorie": "Scroll & Section-Animationen",
 "kategorie_slug": "scroll",
 "kategorie_nr": 5,
 "bau_chat": "C2",
 "prioritaet": "B",
 "trendstatus": "etabliert",
 "belegstatus": "belegt",
 "website_belege": 4,
 "stimmung": [
  "ruhig",
  "präzise",
  "verspielt"
 ],
 "stimmung_roh": [
  "strukturiert",
  "taktil",
  "modern",
  "ruhig",
  "haptisch",
  "geordnet",
  "produktig"
 ],
 "technik": [
  "css",
  "scroll-driven-css",
  "gsap",
  "scrolltrigger",
  "position-sticky"
 ],
 "technik_gruppen": [
  "css",
  "gsap"
 ],
 "leistungskosten": "niedrig",
 "steckbrief": {
  "beschreibung": "Mehrere große Karten (Projekte, Leistungen) sind position: sticky. Beim Scrollen schiebt sich jede Karte über die vorige. Die verdeckten Karten werden dabei leicht kleiner und oft dunkler, so entsteht ein Stapel mit Tiefe. Ein kleiner Versatz im top-Wert pro Karte lässt die Kanten der unteren Karten sichtbar.",
  "warum_es_wirkt": "Die physische Metapher ist sofort klar: Karten werden abgelegt. Jede Karte bekommt kurz die ganze Bühne, der wachsende Stapel zeigt den Fortschritt. Die Mechanik ist einfach, die Wirkung groß.",
  "wann_einsetzen": "Für 3 bis 6 gleichrangige Blöcke: Leistungen, ausgewählte Projekte, Werte, Prozessschritte.",
  "wann_nicht": "Bei Karten mit viel oder wechselnd langem Text, denn Inhalt wird verdeckt. Bei sehr vielen Einträgen und bei Karten, die höher als der Viewport sind. Nicht als Allzweck-Effekt auf jeder Seite: 2026 ist er in Templates sehr verbreitet und wirkt schnell generisch.",
  "varianten": [
   "Herunterskalieren der verdeckten Karten (Standard), z. B. 1 − 0,05 je Ebene",
   "Abdunkeln der verdeckten Karten, z. B. Helligkeit 0,75 hoch Tiefe (Huge)",
   "Kippen nach hinten: verdeckte Karte geht auf scale 0,7 und rotateX 40°, dazu leichte Drehung ±5° (Pieter Koopt)",
   "Kippen mit Verblassen: scale 0,8 und rotateX/Y bis 15°, opacity sinkt mit (Sunmetalon)",
   "Flacher Stapel mit Versatz wie Karteikarten, begrenzt auf 3 sichtbare Ebenen (Pika)",
   "Kurzes Zusammendrücken mit elastischem Rückfedern beim Andocken (Huge)",
   "Bild in der ankommenden Karte zoomt von 2× auf 1× (Olivier Larose)",
   "Reines CSS mit view-timeline und exit-crossing-Ranges",
   "Horizontal stapelnde Karten"
  ],
  "umsetzung": "Struktur: .stapel > .karte (position: sticky; top: calc(Basis + var(--i) × 25px); feste Höhe) > .flaeche. Animiert wird nur das innere Element, damit die Layouthöhe gleich bleibt. Kein overflow: hidden auf Eltern, sonst greift sticky nicht. transform-origin: top center wirkt natürlich.\n\nJS: Pro Karte a = clamp(1 − (Abstand zur Sticky-Position ÷ Kartenhöhe), 0, 1). Die Tiefe einer Karte ist die Summe von a aller folgenden Karten. Daraus scale = 1 − 0,05 × Tiefe und Abdunkeln 1 − 0,75^Tiefe. Berechnung im passiven scroll-Listener mit requestAnimationFrame. Alternativ GSAP ScrollTrigger mit scrub.\n\nNur CSS: #cards { view-timeline-name: --cards } und .card__inner { animation: scale linear forwards; animation-timeline: --cards; animation-range: exit-crossing calc(var(--i0) / var(--n) × 100%) exit-crossing calc(var(--i) / var(--n) × 100%) }. Schrittweite aus der Kartenanzahl ableiten, feste 0,25-Schritte brechen ab 5 Karten.",
  "mobil": "Funktioniert mit sticky auch auf Touch. Karten nicht höher als etwa 80svh, Versatz kleiner, Skalierung schwächer. Bild über den Text stellen und Hochformat-Bilder nutzen.",
  "reduzierte_bewegung": "Nur die sticky-Überlagerung behalten, ohne Skalieren, Kippen, Abdunkeln und Rückfedern. Alternativ als normale Liste untereinander.",
  "barrierefreiheit": "Verdeckte Karten bleiben im DOM und fokussierbar. Bekommt ein Element in einer verdeckten Karte den Fokus, muss die Seite zu dieser Karte scrollen (scroll-margin-top setzen oder per focusin zur Andock-Position springen). Kartenüberschriften als echte Überschriften auszeichnen."
 },
 "messwerte": {
  "easing": "none (Abdunkeln, scrub .5); elastic.out(1, 0.3) beim Andocken; power1.in, scrub (Pieter Koopt); linear zum Abstand (Sunmetalon, Pika)",
  "dauer_ms": "Squash 100, Rückfedern 1000; scrollgebunden",
  "stagger_ms": "",
  "distanz": "Abdunkeln 0.75^Index; verdeckte Karte scale .7 + rotationX 40° (Pieter Koopt); scale .8–1 mit rotateX/Y bis 15° (Sunmetalon); scale 1 − 0.025 je Ebene, max. 3 Ebenen, 3px Versatz (Pika)",
  "sonstiges": "Squash: scaleX (w+1.5em)/w, scaleY (h-0.5em)/h; Sticky-Top 0 (Sunmetalon), 80/84px (Pika); Pin-Start top 20 % bzw. 10 % mobil (Pieter Koopt)",
  "herkunft": "beobachtet",
  "quelle": "https://www.hugeinc.com/_next/static/chunks/2iuon7aqn2-4w.js; https://sunmetalon.com/_nuxt/D5_x4G6o.js",
  "aus_eintrag": [
   "stacking-cards"
  ]
 },
 "kurve": {
  "bezier": [
   0.11,
   0,
   0.5,
   0
  ],
  "css": "cubic-bezier(0.11, 0, 0.5, 0)",
  "name": "power1.in",
  "dauer_ms": 100
 },
 "gesehen_bei": [
  {
   "seite": "scroll-driven-animations.style – Stacking Cards",
   "url": "https://scroll-driven-animations.style/demos/stacking-cards/css/",
   "auszeichnung": "",
   "beobachtung": "Sticky .card__content, Keyframe scale(calc(1.1 - 0.1 * reverse-index)) → bei 4 Karten 0,7/0,8/0,9/1,0; jede Karte bekommt 25 % der exit-crossing-Range.",
   "art": "demo"
  },
  {
   "seite": "Olivier Larose – Cards Parallax",
   "url": "https://blog.olivierlarose.com/tutorials/cards-parallax",
   "auszeichnung": "",
   "beobachtung": "top: calc(-5vh + i*25px), targetScale = 1 - (n - i) * 0.05, Bild in Karte skaliert 2 → 1.",
   "art": "artikel"
  },
  {
   "seite": "Huge",
   "url": "https://www.hugeinc.com/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft (Chunk 2iuon7aqn2-4w.js): [data-projects-stacking-card] sticky; die jeweils vorige Karte wird über --stack-dim auf 0.75^Index abgedunkelt (scrub .5, start top 75 % bis Sticky-Top); beim Andocken kurzer Squash (scaleX +1.5em, .1s power1.out) und Rückfedern elastic.out(1, 0.3) 1s.",
   "art": "website"
  },
  {
   "seite": "Pieter Koopt",
   "url": "https://pieterkoopt.nl/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft 31.10.2026 (HTML/Bundle per curl abgerufen): Inline-Script im HTML (Abschnitt .section_stacking-cards, Klasse mwg_effect031): jede .slide pinnt ihren .content-wrapper (ScrollTrigger pin, start 'top 20%', mobil 'top 10%', end +=innerHeight, scrub true); die Karte (.content) geht dabei auf scale .7, rotationX 40 und zufällige rotationZ ±5°, ease power1.in; ab 'top -80%' blendet sie über 0,2 × Viewporthöhe aus (autoAlpha 0). Die letzte Karte bleibt unverändert.",
   "art": "website"
  },
  {
   "seite": "Sunmetalon",
   "url": "https://sunmetalon.com/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft 31.10.2026 (HTML/Bundle per curl abgerufen): Nuxt-Komponente 'StackingCards' (Chunk /_nuxt/D5_x4G6o.js, CSS StackingCards.DbxLqVOm.css): .card ist position sticky; top 0; transform-origin center bottom. Pro Scroll-Tick wird für jede Karte a = clamp(0,1, Abstand der nächsten Karte zur Oberkante ÷ Kartenhöhe) berechnet: scale .8 + .2·a, opacity a, rotateX und rotateY je 15° − 15°·a, Klasse 'falling' solange a < 1. Zwei Deko-Bilder laufen mit scrub 2 gegenläufig (yPercent −100/rotate −25 bzw. yPercent 100/rotateZ 50).",
   "art": "website"
  },
  {
   "seite": "Pika",
   "url": "https://pika.art/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft 31.10.2026 (HTML/Bundle per curl abgerufen): Chunk 10vjtvr_pr-r9.js: Liste data-mobile-story mit data-stacked; li sticky top var(--card-top) (80px, ab sm 84px). Für verdeckte Karten Tiefe c = min(Überdeckung, 3): scale 1 − 0.025·c (min. 0,925), translateY zusätzlich −3px·c, clip-path inset unten auf sichtbaren Rest; ab Tiefe > 3 opacity 4 − Tiefe. Berechnung per scroll-Listener (passive) plus ResizeObserver/requestAnimationFrame. Laut Attributname auf der Mobil-Darstellung aktiv.",
   "art": "website"
  }
 ],
 "tutorials": [
  {
   "titel": "Stacking Cards (CSS scroll-driven)",
   "url": "https://scroll-driven-animations.style/demos/stacking-cards/css/"
  },
  {
   "titel": "Olivier Larose – Cards Parallax",
   "url": "https://blog.olivierlarose.com/tutorials/cards-parallax"
  },
  {
   "titel": "On-Scroll Animation Ideas for Sticky Sections (Codrops)",
   "url": "https://tympanus.net/codrops/2024/01/31/on-scroll-animation-ideas-for-sticky-sections/"
  },
  {
   "titel": "How to create stacking cards (CodyHouse)",
   "url": "https://codyhouse.co/tutorials/how-stacking-cards"
  }
 ],
 "kombinierbar_mit": [
  "clip-path-bild-reveal",
  "theme-farbwechsel-scroll",
  "split-mask-reveal",
  "split-screen-sticky-seiten",
  "case-study-geruest"
 ],
 "urls": {
  "detail": "https://cecodesign.de/lab/fundus/scroll/stapelkarten/",
  "meta": "https://cecodesign.de/lab/fundus/scroll/stapelkarten/meta.json",
  "demo": "https://cecodesign.de/lab/fundus/scroll/stapelkarten/demo"
 },
 "herkunft": {
  "quelldateien": [
   "scroll.json",
   "layouts.json",
   "patch-belege-3.json",
   "r3-belege-a.json"
  ],
  "pruefnotizen": [
   "patch-belege-3.json: Nur ein live geprüfter Produktionsbeleg (Ziel 2 nicht erreicht); Muster ist verbreitet, daher Prio A belassen. Awwwards-Inspirationen entfernt.",
   "r3-belege-a.json (Runde 3, 31.10.2026): Drei Produktionsseiten mit drei Varianten: Kippen nach hinten (rotationX), Kippen nach hinten mit Verblassen, flaches Herunterskalieren mit Begrenzung auf 3 Ebenen. Pieter Koopt nutzt sichtbar das Made-with-GSAP-Effekt-Muster 031.",
   "02.11.2026: Priorität A → B (Lückenrecherche K2), weil das eigene wann_nicht warnt vor der Sättigung in Templates; trendstatus bleibt etabliert."
  ],
  "zusammengelegt_aus": [
   "stacking-cards",
   "stapelkarten-sticky"
  ],
  "trendstatus_begruendung": "Konflikt abklingend/B (layouts) vs. etabliert/A (scroll). Aufgelöst zu 'etabliert': das Muster ist weiterhin sehr verbreitet und technisch inzwischen rein mit CSS lösbar; 'abklingend' beschrieb die Sättigung in Templates (wirkt als Allzweck-Effekt generisch), nicht einen Rückgang der Nutzung. Die Warnung steht in wann_nicht.",
  "querverweis_kategorien": [
   "Hero & Section-Layouts"
  ]
 },
 "bau": {
  "status": "fertig",
  "kurz": "Projektkarten docken beim Scrollen nacheinander an und legen sich übereinander; die verdeckten werden kleiner und dunkler, kippen weg oder bleiben als Register stehen.",
  "demo": true,
  "abhaengigkeiten": [],
  "werte": {
   "technik": "position: sticky auf .karte, Transform auf dem inneren .flaeche (Layouthöhe bleibt gleich); Fortschritt per passivem scroll-Listener + requestAnimationFrame",
   "fortschritt": "a(j) = clamp(1 − (top_aktuell − top_sticky) ÷ Kartenhöhe, 0, 1); Tiefe d(i) = Summe a(j) aller folgenden Karten",
   "karten": 5,
   "luft_zwischen_karten": "24svh (mobil 18svh)",
   "kartenhoehe": "min(660px, 100svh − Sticky-Top der letzten Karte − 52px); mobil max. 80svh",
   "ablegen": {
    "sticky_top": "60px + 2svh + i × 25px (mobil i × 14px)",
    "scale": "1 − 0,05 × d (Touch: 1 − 0,03 × d)",
    "abdunkeln": "schwarze Ebene, opacity 1 − 0,75^d (entspricht Helligkeit 0,75^d)",
    "transform_origin": "50% 0%",
    "bild_zoom": "1,3 → 1 über a der eigenen Karte",
    "andocken_squash": "scaleX (w + 1.5em) ÷ w, scaleY (h − 0.5em) ÷ h, 100 ms power1.out cubic-bezier(0.25, 0.46, 0.45, 0.94)",
    "andocken_rueckfedern": "elastic.out(1, 0.3) 1000 ms, als CSS linear() mit 61 Punkten (Fallback cubic-bezier(0.16, 1, 0.3, 1))",
    "bild_ausschnitt_karte_1": "Karte 1 (keramik-stillleben-hoch) als Hochformat 4:5 in jeder Breite; object-position 50% clamp(100cqh − 125cqw, 40cqh − 62,5cqw, 0px) (Bildmitte ruht bei 40 % der Bildhöhe), transform-origin 50% 100% für den Zoom 1,3 → 1"
   },
   "kippen": {
    "sticky_top": "60px + 4svh, kein Versatz",
    "easing": "power1.in (e = a²) über a der nächsten Karte",
    "scale": "1 − 0,3 × e (Ende 0,7; Touch 0,82)",
    "rotateX": "40° × e",
    "rotateZ": "je Karte fest −4°, 3°, −5°, 2° (× e)",
    "perspective": "1200px (eigener Wert)",
    "transform_origin": "50% 30%",
    "abdunkeln": "opacity 0,25 × e",
    "ausblenden": "opacity 1 − clamp((d − 1) ÷ 0,2)"
   },
   "karteikasten": {
    "sticky_top": "60px + 1svh + i × 48px (mobil i × 40px), Reiterhöhe 48px bleibt sichtbar",
    "tiefe": "c = min(d, 3)",
    "scale": "1 − 0,025 × c (min. 0,925)",
    "translateY": "−3px × c",
    "abdunkeln": "opacity 0,06 × c",
    "ausblenden": "ab d > 3: opacity 4 − d"
   },
   "vorfuehrung": "Selbst-Scroll von Andock-Punkt zu Andock-Punkt, je 1300 ms easeInOutCubic + 650 ms halten; in der Galerie-Vorschau wechselt die Variante jede Runde",
   "ui": "Umschalter 150 ms Farbe"
  },
  "ausloeser": "scroll",
  "touch": "Gleiches sticky-Verhalten mit nativem Scroll; Skalierung auf 60 % abgeschwächt (--staerke .6 unter hover: none), unter 760px Bild über Text mit Hochformat-Bildern, kleinerer Versatz (14px bzw. 40px), Karten max. 80svh.",
  "reduziert": "Nur die sticky-Überlagerung: kein Skalieren, Kippen, Abdunkeln, kein Bild-Zoom und kein Squash. Die Vorführung springt ohne Gleiten von Karte zu Karte.",
  "korrekturen": {
   "beschreibung": "Mehrere große Karten (Projekte, Leistungen) sind position: sticky. Beim Scrollen schiebt sich jede Karte über die vorige. Die verdeckten Karten werden dabei leicht kleiner und oft dunkler, so entsteht ein Stapel mit Tiefe. Ein kleiner Versatz im top-Wert pro Karte lässt die Kanten der unteren Karten sichtbar.",
   "warum_es_wirkt": "Die physische Metapher ist sofort klar: Karten werden abgelegt. Jede Karte bekommt kurz die ganze Bühne, der wachsende Stapel zeigt den Fortschritt. Die Mechanik ist einfach, die Wirkung groß.",
   "wann_einsetzen": "Für 3 bis 6 gleichrangige Blöcke: Leistungen, ausgewählte Projekte, Werte, Prozessschritte.",
   "wann_nicht": "Bei Karten mit viel oder wechselnd langem Text, denn Inhalt wird verdeckt. Bei sehr vielen Einträgen und bei Karten, die höher als der Viewport sind. Nicht als Allzweck-Effekt auf jeder Seite: 2026 ist er in Templates sehr verbreitet und wirkt schnell generisch.",
   "varianten": [
    "Herunterskalieren der verdeckten Karten (Standard), z. B. 1 − 0,05 je Ebene",
    "Abdunkeln der verdeckten Karten, z. B. Helligkeit 0,75 hoch Tiefe (Huge)",
    "Kippen nach hinten: verdeckte Karte geht auf scale 0,7 und rotateX 40°, dazu leichte Drehung ±5° (Pieter Koopt)",
    "Kippen mit Verblassen: scale 0,8 und rotateX/Y bis 15°, opacity sinkt mit (Sunmetalon)",
    "Flacher Stapel mit Versatz wie Karteikarten, begrenzt auf 3 sichtbare Ebenen (Pika)",
    "Kurzes Zusammendrücken mit elastischem Rückfedern beim Andocken (Huge)",
    "Bild in der ankommenden Karte zoomt von 2× auf 1× (Olivier Larose)",
    "Reines CSS mit view-timeline und exit-crossing-Ranges",
    "Horizontal stapelnde Karten"
   ],
   "umsetzung": "Struktur: .stapel > .karte (position: sticky; top: calc(Basis + var(--i) × 25px); feste Höhe) > .flaeche. Animiert wird nur das innere Element, damit die Layouthöhe gleich bleibt. Kein overflow: hidden auf Eltern, sonst greift sticky nicht. transform-origin: top center wirkt natürlich.\n\nJS: Pro Karte a = clamp(1 − (Abstand zur Sticky-Position ÷ Kartenhöhe), 0, 1). Die Tiefe einer Karte ist die Summe von a aller folgenden Karten. Daraus scale = 1 − 0,05 × Tiefe und Abdunkeln 1 − 0,75^Tiefe. Berechnung im passiven scroll-Listener mit requestAnimationFrame. Alternativ GSAP ScrollTrigger mit scrub.\n\nNur CSS: #cards { view-timeline-name: --cards } und .card__inner { animation: scale linear forwards; animation-timeline: --cards; animation-range: exit-crossing calc(var(--i0) / var(--n) × 100%) exit-crossing calc(var(--i) / var(--n) × 100%) }. Schrittweite aus der Kartenanzahl ableiten, feste 0,25-Schritte brechen ab 5 Karten.",
   "mobil": "Funktioniert mit sticky auch auf Touch. Karten nicht höher als etwa 80svh, Versatz kleiner, Skalierung schwächer. Bild über den Text stellen und Hochformat-Bilder nutzen.",
   "reduzierte_bewegung": "Nur die sticky-Überlagerung behalten, ohne Skalieren, Kippen, Abdunkeln und Rückfedern. Alternativ als normale Liste untereinander.",
   "barrierefreiheit": "Verdeckte Karten bleiben im DOM und fokussierbar. Bekommt ein Element in einer verdeckten Karte den Fokus, muss die Seite zu dieser Karte scrollen (scroll-margin-top setzen oder per focusin zur Andock-Position springen). Kartenüberschriften als echte Überschriften auszeichnen."
  },
  "geprueft": "01.11.2026",
  "notiz": "Portfolio-Ausschnitt eines erfundenen Grafikbüros mit fünf Arbeiten aus medien/ (drei Marken, Keramik, Flakon). Drei Varianten per Umschalter: Ablegen (Larose-Skalierung, Huge-Abdunkeln und Squash beim Andocken), Kippen (Pieter Koopt) und Karteikasten (Pika, Reiter bleiben sichtbar). Reines JS ohne Bibliothek; die Werte-Zeile unten zeigt die aktive Variante, rechts zählt sie die abgelegten Karten. Bild-Zoom bewusst 1,3 → 1 statt 2 → 1, weil Logos sonst angeschnitten wären. Korrektur 01.11.2026: Die erste Karte startete als reine Farbfläche, weil das zentrierte Motiv bei Bild-Scale 1,3 unter dem Bildschirmrand bzw. am Rahmen lag. Jetzt nutzt sie überall das Hochformat mit einem Ausschnitt, der die Bildmitte bei 40 % der Bildhöhe hält (Container-Query-Einheiten am Bildrahmen), und zoomt vom unteren Rand aus. So ist das Motiv schon in der angeschnittenen Startansicht (1440 × 900 und 390 px) ganz zu sehen und liegt angedockt auf Höhe der Überschrift. Zoomwert 1,3 → 1 bleibt. Review 01.11.2026: In der Galerie-Vorschau lag die Werte-Zeile über Text und Schlagworten der Karte; sie ist dort jetzt ausgeblendet. Bilder 01.11.2026: eigene Bilder statt Portfolio. Zugang 02.11.2026: Leise Labels der hellen Karten Alpha .58/.6 → .66/.68 (≥ 5,1:1); Tastaturfokus in einer Karte scrollt zu ihrer Andockposition, damit die nächste Karte den Link nicht verdeckt; unter 380 px Beschreibung ohne Zeilenbegrenzung; Kontrastmodus-Rahmen für Knöpfe."
 }
}
