{
 "$schema": "../../schema.json",
 "schema": "fundus/1",
 "id": "vt-shared-element-morph",
 "name": "Vorschaubild wird zur Hero (Shared-Element-Morph)",
 "name_en": "Shared element transition / thumbnail-to-hero morph",
 "kurz": "Das angeklickte Vorschaubild fliegt aus dem Raster genau an die Stelle des Hero-Bilds, erst danach steigt der Titel auf; nativ per View Transition oder mit GSAP Flip.",
 "kategorie": "Seitenübergänge",
 "kategorie_slug": "seitenuebergaenge",
 "kategorie_nr": 1,
 "bau_chat": "C3",
 "prioritaet": "A",
 "trendstatus": "etabliert",
 "belegstatus": "belegt",
 "website_belege": 3,
 "stimmung": [
  "ruhig",
  "editorial",
  "präzise",
  "luxuriös"
 ],
 "stimmung_roh": [
  "editorial",
  "luxuriös",
  "ruhig",
  "präzise"
 ],
 "technik": [
  "view-transitions",
  "gsap",
  "flip",
  "barba",
  "css"
 ],
 "technik_gruppen": [
  "css",
  "gsap"
 ],
 "leistungskosten": "mittel",
 "steckbrief": {
  "beschreibung": "Klick auf ein Vorschaubild im Raster. Zuerst blenden alle anderen Inhalte aus (300 bis 500 ms). Dann löst sich das angeklickte Bild aus dem Raster und wächst in Position und Größe genau dorthin, wo auf der Detailseite das Hero-Bild sitzt. Dabei geht der Kartenradius auf 0, oft mit leichtem Innen-Zoom (Bild-Scale 1.1 auf 1.0). Erst danach erscheinen Titel und Inhalt der Detailseite. Zurück läuft alles spiegelbildlich ins Raster.",
  "warum_es_wirkt": "Objektpermanenz: Das Auge verfolgt ein einziges Objekt. Der Ortswechsel wird räumlich verständlich, und das Bild bekommt Gewicht. Der Übergang gilt als Königsdisziplin im Portfolio.",
  "wann_einsetzen": "Bei Portfolio-, Case-Study-, Shop- und Galerieseiten, auf denen ein Listenelement auf der Detailseite als Hero wiederkehrt.",
  "wann_nicht": "Wenn Vorschau und Hero unterschiedliche Motive zeigen. Bei stark abweichenden Seitenverhältnissen wirkt der Morph verzerrt, solange man nicht mit object-fit gegensteuert. Auch nicht, wenn das Vorschaubild beim Klick außerhalb des Viewports liegt.",
  "varianten": [
   "Nativ: gleicher view-transition-name auf Thumbnail und Hero (MPA oder SPA)",
   "GSAP Flip: Das Element wird per Flip.getState und Flip.from in den Hero-Container umgehängt (mit Barba oder Taxi)",
   "React <ViewTransition name=…> mit share-Animation",
   "Mit Nested View Transition Groups (view-transition-group), damit das Bild im Karten-Container geclippt bleibt",
   "Raster zu Slideshow oder Raster zu Vollbild innerhalb einer Seite (Codrops-Layout-Animationen)"
  ],
  "umsetzung": "Nativ über Seiten hinweg: In beiden Seiten @view-transition { navigation: auto; }. Den Namen nur am angeklickten Vorschaubild setzen (im Klick-Handler oder im pageswap-Event). Auf der Zielseite trägt das Hero-Bild denselben Namen, z. B. view-transition-name: hero. In einer SPA genügt document.startViewTransition(() => seiteWechseln()). Feinjustieren mit ::view-transition-group(hero) { animation-duration: 1.3s; animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1); overflow: clip; }. Bei abweichendem Seitenverhältnis ::view-transition-old(hero), ::view-transition-new(hero) { height: 100%; object-fit: cover; }. Zeigen beide Schnappschüsse dasselbe Motiv, den Crossfade abschalten (animation: none) und nur einen sichtbar lassen, sonst entstehen Doppelbilder. Radius und Innen-Zoom lassen sich nach transition.ready per document.documentElement.animate(…, { pseudoElement: '::view-transition-group(hero)' }) ergänzen. GSAP: const state = Flip.getState(img); heroContainer.appendChild(img); Flip.from(state, { duration: 1.3, ease: 'power3.inOut', absolute: true }). Fallstricke: Namen müssen pro Seite eindeutig sein (React meldet sonst einen Duplicate-Name-Fehler). Liegt das Element außerhalb des Viewports, bildet sich kein Paar. Ungeladene Bilder verzögern den Start, deshalb vorladen (Hover-Preload wie bei Thibault Guignand).",
  "mobil": "Funktioniert gut. Auf kleinen Bildschirmen ist der Weg kürzer, deshalb die Dauer auf 600 bis 900 ms senken (typisch).",
  "reduzierte_bewegung": "Morph durch einen kurzen Crossfade ersetzen: Namen nicht vergeben oder Flip mit duration: 0.",
  "barrierefreiheit": "Nach dem Wechsel den Fokus auf die H1 der Detailseite setzen, bei SPA-Routern ist das Pflicht. Beim Zurück den Fokus auf das Vorschaubild legen, von dem man kam. Alt-Texte von Vorschaubild und Hero gleich halten."
 },
 "messwerte": {
  "easing": "power3.inOut",
  "dauer_ms": "1300",
  "stagger_ms": "100 je Titelzeile",
  "distanz": "Grid-Position bis Hero-Position",
  "sonstiges": "Thumbnail-Bild intern Scale 1.1 auf 1.0 ist typisch, nicht beobachtet",
  "herkunft": "beobachtet",
  "quelle": "https://tympanus.net/codrops/2025/03/25/stas-bondar-25-the-code-techniques-behind-a-next-level-portfolio/"
 },
 "kurve": {
  "bezier": [
   0.76,
   0,
   0.24,
   1
  ],
  "css": "cubic-bezier(0.76, 0, 0.24, 1)",
  "name": "power3.inOut",
  "dauer_ms": 1300
 },
 "gesehen_bei": [
  {
   "seite": "Stas Bondar Portfolio ’25",
   "url": "https://tympanus.net/codrops/2025/03/25/stas-bondar-25-the-code-techniques-behind-a-next-level-portfolio/",
   "auszeichnung": "",
   "beobachtung": "Barba.js plus GSAP Flip: Das angeklickte Thumbnail wird in den Hero-Container der Case Study verschoben und per Flip.from über 1,3 s mit power3.inOut gemorpht. Danach erscheint der Titel per SplitText mit Scramble und 0,1 s Stagger je Zeile.",
   "art": "artikel"
  },
  {
   "seite": "Joffrey Spitzer Portfolio (Astro + GSAP)",
   "url": "https://tympanus.net/codrops/2026/02/18/joffrey-spitzer-portfolio-a-minimalist-astro-gsap-build-with-reveals-flip-transitions-and-subtle-motion/",
   "auszeichnung": "",
   "beobachtung": "Laut Titel und Codrops-Teaser ein minimalistisches Portfolio mit Flip-Übergängen und Text-Reveals. Der Artikel war nicht abrufbar, Details im Browser prüfen.",
   "art": "artikel"
  },
  {
   "seite": "Piccalilli: Galerie-Beispiel",
   "url": "https://piccalil.li/blog/some-practical-examples-of-view-transitions-to-elevate-your-ui/",
   "auszeichnung": "",
   "beobachtung": "Galerie zu Detailseite als Cross-document-Übergang. Die Bilder tragen view-transition-name image-1, image-2 usw.",
   "art": "artikel"
  },
  {
   "seite": "Una Kravets (una.im)",
   "url": "https://una.im/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft: Blog-Karten setzen inline style=\"view-transition-name: <slug>-img\" bzw. \"<slug>-title\"; mit @view-transition{navigation:auto} morphen Bild und Titel seitenübergreifend in den Artikelkopf.",
   "art": "website"
  },
  {
   "seite": "nerdy.dev (Adam Argyle)",
   "url": "https://nerdy.dev/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft (view-transitions.js, style.css): pageswap/pagereveal vergeben temporär view-transition-names für die angeklickte Karte (setTemporaryViewTransitionNames), view-transition-class morph-slide/morph-scale; CSS-Fallback über scroll-driven Namensvergabe.",
   "art": "website"
  },
  {
   "seite": "Joffrey Spitzer Portfolio",
   "url": "https://joffreyspitzer.com/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft (layout…QNTHFiVA.js, swup): beim Klick wird das gewählte Thumbnail in sessionStorage \"folio26:selected-thumb\" gemerkt und der Projekttitel geklont (cloneProjectTitle); GSAP Flip übernimmt den Morph.",
   "art": "website"
  }
 ],
 "tutorials": [
  {
   "titel": "Ideas for Grid to Slideshow Switch Animations (Codrops)",
   "url": "https://tympanus.net/codrops/2023/05/04/ideas-for-grid-to-slideshow-switch-animations/"
  },
  {
   "titel": "React <ViewTransition> Referenz (share, name)",
   "url": "https://react.dev/reference/react/ViewTransition.md"
  },
  {
   "titel": "Osmo Page Transition Course (Grid-to-detail mit GSAP Flip)",
   "url": "https://www.osmo.supply/product/page-transition-course"
  }
 ],
 "kombinierbar_mit": [
  "vt-cross-document-mpa",
  "split-mask-reveal",
  "richtungsbewusste-uebergaenge",
  "parallel-crossfade-preload"
 ],
 "urls": {
  "detail": "https://cecodesign.de/lab/fundus/seitenuebergaenge/vt-shared-element-morph/",
  "meta": "https://cecodesign.de/lab/fundus/seitenuebergaenge/vt-shared-element-morph/meta.json",
  "demo": "https://cecodesign.de/lab/fundus/seitenuebergaenge/vt-shared-element-morph/demo"
 },
 "herkunft": {
  "quelldateien": [
   "uebergaenge.json",
   "patch-belege-3.json"
  ],
  "pruefnotizen": [
   "patch-belege-3.json: Zwei native Cross-Document-Morphs plus eine GSAP-Flip-Variante."
  ]
 },
 "bau": {
  "status": "fertig",
  "kurz": "Das angeklickte Vorschaubild fliegt aus dem Raster genau an die Stelle des Hero-Bilds, erst danach steigt der Titel auf; nativ per View Transition oder mit GSAP Flip.",
  "demo": true,
  "abhaengigkeiten": [
   "gsap@3.15.0",
   "Flip@3.15.0"
  ],
  "werte": {
   "easing_morph": "cubic-bezier(0.76, 0, 0.24, 1) (power3.inOut)",
   "dauer_morph_ms": 1300,
   "dauer_morph_telefon_ms": "900 (unter 640 px Viewport-Breite)",
   "ablauf_auf": "1) übrige Inhalte 400 ms weg, 2) Morph 1300 ms, 3) Titel ab 70 % des Morphs",
   "ausblenden": "alles außer dem gewählten Bild: opacity 1 → 0, translateY 0 → 18px, 400 ms, cubic-bezier(0.76, 0, 0.24, 1)",
   "bild_scale": "1.1 → 1 (Vorschaubild intern 1.1, Hero 1.0), gleiche Dauer und Kurve wie der Morph",
   "radius": "6px → 0px, gleiche Dauer und Kurve",
   "titel": "zwei Zeilen in overflow: hidden, translateY 110 % → 0, 900 ms cubic-bezier(0.16, 1, 0.3, 1), Stagger 100 ms je Zeile",
   "inhalt": "opacity 0 → 1, translateY 20px → 0, 900 ms cubic-bezier(0.16, 1, 0.3, 1), Verzögerung 0 / 250 / 320 / 420 ms",
   "ablauf_zu": "Inhalt 400 ms weg (cubic-bezier(0.76, 0, 0.24, 1)), Morph 1300 ms zurück, dann Raster opacity und translate 600 ms cubic-bezier(0.16, 1, 0.3, 1), Stagger 40 ms",
   "view_transition": "view-transition-name: hero nur am geklickten Element; ::view-transition-group(hero) { overflow: clip; animation-duration: 1300ms; animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1) }; ::view-transition-old/new(hero) { height: 100%; object-fit: cover; animation: none }, sichtbar bleibt nur der Hero-Schnappschuss (kein Crossfade)",
   "view_transition_extras": "nach transition.ready: document.documentElement.animate({ borderRadius }, { pseudoElement: '::view-transition-group(hero)' }) und transform scale auf ::view-transition-new(hero) bzw. -old(hero)",
   "view_transition_root": "::view-transition-old/new(root) 400 ms, cubic-bezier(0.76, 0, 0.24, 1)",
   "flip": "Flip.getState(el, { props: 'borderRadius' }) → heroContainer.appendChild(el) → Flip.from(state, { duration: 1.3, ease: 'power3.inOut', absolute: true, zIndex: 30, props: 'borderRadius' }); Bild parallel gsap.fromTo(img, { scale: 1.1 }, { scale: 1 })",
   "bildformat": "object-fit: contain; Motiv 3:4 (SVG 600 × 800) auf seiner Wand- und Bodenfarbe (linear-gradient, Teilung bei 82 % wie im Motiv), der Hintergrund liegt auf dem img und skaliert mit; Vorschaubild 3:4, Hero volle Breite, Höhe clamp(240px, 54svh, 620px), dort setzt sich der Raum seitlich fort",
   "hover_karte": "Bild scale 1.1 → 1.04, 900 ms cubic-bezier(0.16, 1, 0.3, 1)",
   "reduziert": "Crossfade opacity 0 → 1, 200 ms linear"
  },
  "ausloeser": "klick",
  "touch": "Tippen auf eine Karte öffnet den Look, „Kollektion“ (auf dem Telefon rechts oben) führt zurück. Kein Hover-Zoom, Pfeile immer sichtbar, Umschalter mit 34 px hohen Knöpfen. Unter 640 px Breite dauert der Morph 900 statt 1300 ms.",
  "reduziert": "Kein Morph, kein Ausblenden, keine Titelbewegung: die Seiten wechseln per Crossfade (opacity 0 → 1, 200 ms linear). Das Vorschaubild hat keinen Innen-Zoom. Die Werte-Anzeige nennt das.",
  "korrekturen": {
   "beschreibung": "Klick auf ein Vorschaubild im Raster. Zuerst blenden alle anderen Inhalte aus (300 bis 500 ms). Dann löst sich das angeklickte Bild aus dem Raster und wächst in Position und Größe genau dorthin, wo auf der Detailseite das Hero-Bild sitzt. Dabei geht der Kartenradius auf 0, oft mit leichtem Innen-Zoom (Bild-Scale 1.1 auf 1.0). Erst danach erscheinen Titel und Inhalt der Detailseite. Zurück läuft alles spiegelbildlich ins Raster.",
   "warum_es_wirkt": "Objektpermanenz: Das Auge verfolgt ein einziges Objekt. Der Ortswechsel wird räumlich verständlich, und das Bild bekommt Gewicht. Der Übergang gilt als Königsdisziplin im Portfolio.",
   "wann_einsetzen": "Bei Portfolio-, Case-Study-, Shop- und Galerieseiten, auf denen ein Listenelement auf der Detailseite als Hero wiederkehrt.",
   "wann_nicht": "Wenn Vorschau und Hero unterschiedliche Motive zeigen. Bei stark abweichenden Seitenverhältnissen wirkt der Morph verzerrt, solange man nicht mit object-fit gegensteuert. Auch nicht, wenn das Vorschaubild beim Klick außerhalb des Viewports liegt.",
   "varianten": [
    "Nativ: gleicher view-transition-name auf Thumbnail und Hero (MPA oder SPA)",
    "GSAP Flip: Das Element wird per Flip.getState und Flip.from in den Hero-Container umgehängt (mit Barba oder Taxi)",
    "React <ViewTransition name=…> mit share-Animation",
    "Mit Nested View Transition Groups (view-transition-group), damit das Bild im Karten-Container geclippt bleibt",
    "Raster zu Slideshow oder Raster zu Vollbild innerhalb einer Seite (Codrops-Layout-Animationen)"
   ],
   "umsetzung": "Nativ über Seiten hinweg: In beiden Seiten @view-transition { navigation: auto; }. Den Namen nur am angeklickten Vorschaubild setzen (im Klick-Handler oder im pageswap-Event). Auf der Zielseite trägt das Hero-Bild denselben Namen, z. B. view-transition-name: hero. In einer SPA genügt document.startViewTransition(() => seiteWechseln()). Feinjustieren mit ::view-transition-group(hero) { animation-duration: 1.3s; animation-timing-function: cubic-bezier(0.76, 0, 0.24, 1); overflow: clip; }. Bei abweichendem Seitenverhältnis ::view-transition-old(hero), ::view-transition-new(hero) { height: 100%; object-fit: cover; }. Zeigen beide Schnappschüsse dasselbe Motiv, den Crossfade abschalten (animation: none) und nur einen sichtbar lassen, sonst entstehen Doppelbilder. Radius und Innen-Zoom lassen sich nach transition.ready per document.documentElement.animate(…, { pseudoElement: '::view-transition-group(hero)' }) ergänzen. GSAP: const state = Flip.getState(img); heroContainer.appendChild(img); Flip.from(state, { duration: 1.3, ease: 'power3.inOut', absolute: true }). Fallstricke: Namen müssen pro Seite eindeutig sein (React meldet sonst einen Duplicate-Name-Fehler). Liegt das Element außerhalb des Viewports, bildet sich kein Paar. Ungeladene Bilder verzögern den Start, deshalb vorladen (Hover-Preload wie bei Thibault Guignand).",
   "mobil": "Funktioniert gut. Auf kleinen Bildschirmen ist der Weg kürzer, deshalb die Dauer auf 600 bis 900 ms senken (typisch).",
   "reduzierte_bewegung": "Morph durch einen kurzen Crossfade ersetzen: Namen nicht vergeben oder Flip mit duration: 0.",
   "barrierefreiheit": "Nach dem Wechsel den Fokus auf die H1 der Detailseite setzen, bei SPA-Routern ist das Pflicht. Beim Zurück den Fokus auf das Vorschaubild legen, von dem man kam. Alt-Texte von Vorschaubild und Hero gleich halten."
  },
  "geprueft": "01.11.2026",
  "notiz": "Neue Welt (01.11.2026): erfundenes Modelabel „Atelier Saum, Wien“ mit Lookbook der Kollektion „Fadenlauf“ (vier Looks) und Produktdetailseite (Formelzeile Material + Preis, Größen in Tabellenziffern, Bildunterschrift klein in der Ecke). Gestaltung nach Rezept mode-beauty-kollektion: Zalando Sans (wdth 125, wght 450/250, nicht fett) für alles, Fragment Mono für Rubriken, warmes Off-White #f6f3ee mit Fast-Schwarz #121010; Farbe kommt nur aus den Motiven. Die Kampagnenbilder sind eigene SVG-Illustrationen (gesichtslose Figurinen in Mantel, Strickkleid, Plisseerock, Hosenanzug vor farbigen Wänden), keine medien/-Bilder. Bewegungswerte unverändert. Der Seitenwechsel ist simuliert (zwei Zustände); dasselbe Bild-Element wird ins Hero umgehängt, per same-document View Transition oder GSAP Flip (Umschalter). Für echte Seiten @view-transition { navigation: auto; } und gleicher view-transition-name auf Vorschaubild und Hero. Weil das Motiv per contain auf seiner Raumfarbe steht, zeigt der Hero-Schnappschuss im Kartenformat dasselbe wie das Vorschaubild, ein Crossfade ist unnötig. Escape schließt, Fokus wandert auf H1 bzw. zurück zur Karte. Ohne View-Transition-Unterstützung nutzt die VT-Variante Flip. Vorführung: Zeiger öffnet und schließt nacheinander alle Looks, Technik im Wechsel."
 }
}
