{
 "$schema": "../../schema.json",
 "schema": "fundus/1",
 "id": "theme-umschalter-view-transition",
 "name": "Theme- und Sprachumschalter mit View-Transition-Kreis-Reveal",
 "name_en": "Theme toggle with circular View Transition reveal / Language / theme switcher with circular view transition",
 "kurz": "Hell, Dunkel oder Sprache wechseln, und der neue Zustand breitet sich als Kreis vom Klickpunkt über die Seite aus.",
 "kategorie": "Navigation",
 "kategorie_slug": "navigation",
 "kategorie_nr": 3,
 "bau_chat": "C3",
 "prioritaet": "A",
 "trendstatus": "etabliert",
 "belegstatus": "belegt",
 "website_belege": 3,
 "stimmung": [
  "präzise",
  "verspielt"
 ],
 "stimmung_roh": [
  "verspielt",
  "modern",
  "präzise",
  "technisch"
 ],
 "technik": [
  "view-transitions",
  "css",
  "vanilla-js",
  "clip-path"
 ],
 "technik_gruppen": [
  "css",
  "js"
 ],
 "leistungskosten": "niedrig",
 "steckbrief": {
  "beschreibung": "Ein Klick auf den Theme-Schalter oder den Sprachlink merkt sich die Klickposition. document.startViewTransition() friert den alten Zustand als Bild ein, dann wird umgeschaltet. Der neue Zustand wird per clip-path: circle() vom Klickpunkt aus aufgezogen, bis der Kreis die entfernteste Ecke erreicht. Der alte Zustand bleibt darunter stehen. Die Wahl wird gespeichert und vor dem ersten Paint angewendet, damit nichts aufblitzt.",
  "warum_es_wirkt": "Ein banaler Schalter wird zu einem kleinen Moment. Die neue Farbe kommt sichtbar vom Knopf, Ursache und Wirkung sind sofort klar.",
  "wann_einsetzen": "Portfolio- und Produktseiten mit gepflegtem Hell- und Dunkel-Design. Mehrsprachige Seiten, auf denen der Sprachwechsel ohne Neuladen passiert. Andere Modus-Wechsel, etwa ein Persona- oder Farbschema-Umschalter.",
  "wann_nicht": "Ohne sauberes Token-System für beide Themes, sonst entstehen Mischzustände. Wenn kein echtes Dark Theme gepflegt wird. Wenn der Wechsel sehr oft passiert, der Effekt nutzt sich ab. Bei sehr großen Seiten mit vielen Bildern kostet der Schnappschuss spürbar Zeit.",
  "varianten": [
   "Kreis vom Klickpunkt (Standard, 420 ms ease-out)",
   "Wipe von oben oder links über inset() statt circle()",
   "Kurze Überblendung (ca. 200 ms) für ruhige Seiten",
   "Drei Zustände System, Hell, Dunkel als Auswahl, etwa im Footer (Vercel)",
   "Sprachumschalter als dezenter Textlink in der Zielsprache statt Flaggen (Locomotive, Your Majesty)",
   "Light und Dark pro Komponente statt für die ganze Seite",
   "light-dark() mit Bildern (Chrome 150): ein Satz Farben und Bilder, umgeschaltet wird nur color-scheme"
  ],
  "umsetzung": "Beim Klick x und y aus clientX/clientY nehmen, bei Tastatur (event.detail 0) die Mitte des Knopfs. Radius r = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y)). Ohne document.startViewTransition oder bei reduzierter Bewegung direkt umschalten. Sonst const vt = document.startViewTransition(umschalten); vt.ready.then(() => document.documentElement.animate({ clipPath: [`circle(0px at ${x}px ${y}px)`, `circle(${r}px at ${x}px ${y}px)`] }, { duration: 420, easing: 'cubic-bezier(0, 0, 0.58, 1)', pseudoElement: '::view-transition-new(root)' })). CSS: ::view-transition-old(root), ::view-transition-new(root) { animation: none; mix-blend-mode: normal; }. Themes nur über Tokens wechseln (data-theme am html) und Farben nicht transitionieren, sonst fotografiert die Transition einen Zwischenstand. Wahl in localStorage speichern (try/catch), color-scheme setzen und per Inline-Script im head vor dem ersten Paint anwenden. Das storage-Event gleicht andere Tabs ab. Auf einer echten mehrsprachigen Seite mit eigenen URLs braucht der Sprachwechsel Cross-document View Transitions (@view-transition { navigation: auto; }); der Kreis lässt sich dann über pageswap und pagereveal mit der gespeicherten Klickposition nachbauen.",
  "mobil": "Funktioniert identisch, der Kreis startet am Tap-Punkt. Ziele mindestens 44 px groß.",
  "reduzierte_bewegung": "Sofortwechsel ohne View Transition. Zusätzlich alle ::view-transition-* per prefers-reduced-motion auf animation: none setzen.",
  "barrierefreiheit": "Theme-Knopf mit Namen, der das Ziel nennt („Zu dunklem Design wechseln“). Beide Icons im Markup, das inaktive mit display: none. Immer eine Rückkehr zu „System“ anbieten, am besten als Radiogruppe. Sprachlink mit lang und hreflang, Sprachname in der eigenen Sprache („English“, „Deutsch“). Bei Tastaturbedienung startet der Kreis in der Mitte des Bedienelements."
 },
 "messwerte": {
  "easing": "ease-out",
  "dauer_ms": "420",
  "stagger_ms": "",
  "distanz": "Radius bis zur entferntesten Viewport-Ecke (Math.hypot)",
  "sonstiges": "Custom-View-Transition-Animationen mit prefers-reduced-motion absichern.",
  "herkunft": "beobachtet",
  "quelle": "https://raxxo.shop/blogs/lab/view-transitions-api-5-patterns-i-use-across-raxxo-sites-in-2026",
  "aus_eintrag": [
   "sprach-und-themen-umschalter"
  ]
 },
 "kurve": {
  "bezier": [
   0.0,
   0.0,
   0.58,
   1.0
  ],
  "css": "cubic-bezier(0, 0, 0.58, 1)",
  "name": "ease-out",
  "dauer_ms": 420
 },
 "gesehen_bei": [
  {
   "seite": "RAXXO Lab: View Transitions API – 5 Patterns (05/2026)",
   "url": "https://raxxo.shop/blogs/lab/view-transitions-api-5-patterns-i-use-across-raxxo-sites-in-2026",
   "auszeichnung": "",
   "beobachtung": "Theme-Kreis-Reveal mit startViewTransition, Radius via Math.hypot, Animation 420 ms ease-out; Crossfade-State ~200 ms.",
   "art": "artikel"
  },
  {
   "seite": "Chrome Developers: Dark mode toggle",
   "url": "https://developer.chrome.com/docs/modern-web-guidance/guides/ui-components/dark-mode-toggle",
   "auszeichnung": "",
   "beobachtung": "Offizielles Zustandsmodell: color-scheme per meta, localStorage mit try/catch, storage-Event für Tabs, Inline-Script gegen FOUC, immer zurück zu ‚System‘ erlauben.",
   "art": "artikel"
  },
  {
   "seite": "Chrome Developers: Same-document transitions",
   "url": "https://developer.chrome.com/docs/modern-web-guidance/guides/ui-behaviors/same-document-transitions",
   "auszeichnung": "",
   "beobachtung": "startViewTransition mit Fallback und Reduced-Motion-Regel für ::view-transition-*.",
   "art": "artikel"
  },
  {
   "seite": "Vercel",
   "url": "https://www.vercel.com",
   "auszeichnung": "",
   "beobachtung": "Theme-Auswahl „Select a display theme“ mit system, light, dark in der Footer-Leiste.",
   "art": "website",
   "browser_gemessen": true
  },
  {
   "seite": "Locomotive",
   "url": "https://locomotive.ca/en",
   "auszeichnung": "Awwwards SOTM 03/2023",
   "beobachtung": "Sprachlink „Français“ im Header und im Footer.",
   "art": "website",
   "browser_gemessen": true
  },
  {
   "seite": "Your Majesty",
   "url": "https://yourmajesty.co/",
   "auszeichnung": "",
   "beobachtung": "„EN/NL Nederlands“ im Menü.",
   "art": "website"
  },
  {
   "seite": "Awwwards Inspiration: Mode switch (Eyka)",
   "url": "https://www.awwwards.com/inspiration/mode-switch-eyka",
   "auszeichnung": "Awwwards Inspiration (Element-Sammlung)",
   "beobachtung": "Nur Titel gesehen.",
   "art": "website-ungeprueft"
  },
  {
   "seite": "Awwwards Inspiration: Light vs dark mode (Morten Stig Christensen)",
   "url": "https://www.awwwards.com/inspiration/light-vs-dark-mode-morten-stig-christensen",
   "auszeichnung": "Awwwards Inspiration (Element-Sammlung)",
   "beobachtung": "Nur Titel gesehen.",
   "art": "website-ungeprueft"
  }
 ],
 "tutorials": [
  {
   "titel": "Chrome: Dark mode toggle",
   "url": "https://developer.chrome.com/docs/modern-web-guidance/guides/ui-components/dark-mode-toggle"
  },
  {
   "titel": "RAXXO: View Transitions – 5 Patterns",
   "url": "https://raxxo.shop/blogs/lab/view-transitions-api-5-patterns-i-use-across-raxxo-sites-in-2026"
  },
  {
   "titel": "DEV: A modern approach of implementing dark mode",
   "url": "https://dev.to/renekaesler/a-modern-approach-of-implementing-dark-mode-20f8"
  }
 ],
 "kombinierbar_mit": [
  "toggle-switch",
  "token-federnd-verspielt",
  "gleitender-indikator",
  "live-uhr-und-standort",
  "mega-footer-sitemap"
 ],
 "urls": {
  "detail": "https://cecodesign.de/lab/fundus/navigation/theme-umschalter-view-transition/",
  "meta": "https://cecodesign.de/lab/fundus/navigation/theme-umschalter-view-transition/meta.json",
  "demo": "https://cecodesign.de/lab/fundus/navigation/theme-umschalter-view-transition/demo"
 },
 "herkunft": {
  "quelldateien": [
   "hover.json",
   "navigation.json"
  ],
  "zusammengelegt_aus": [
   "theme-toggle-view-transition",
   "sprach-und-themen-umschalter"
  ],
  "trendstatus_begruendung": "Konflikt etabliert (hover/Mikro) vs. aufsteigend (navigation). Aufgelöst zu 'etabliert': der Kreis-Reveal per startViewTransition ist seit 2023 weit verbreitet und Same-document View Transitions sind seit Oktober 2025 Baseline.",
  "querverweis_kategorien": [
   "Mikroanimationen"
  ],
  "leistungskosten_spanne": "niedrig–mittel (je nach Variante)"
 },
 "bau": {
  "status": "fertig",
  "kurz": "Hell, Dunkel oder Sprache wechseln, und der neue Zustand breitet sich als Kreis vom Klickpunkt über die Seite aus.",
  "demo": true,
  "abhaengigkeiten": [],
  "werte": {
   "technik": "document.startViewTransition(aenderung); in vt.ready document.documentElement.animate({ clipPath }, { pseudoElement: '::view-transition-new(root)' })",
   "easing": "cubic-bezier(0, 0, 0.58, 1)",
   "dauer_ms": 420,
   "clip_kreis": "circle(0px at x y) → circle(r at x y)",
   "radius": "r = Math.hypot(max(x, innerWidth - x), max(y, innerHeight - y))",
   "ursprung": "Klick- bzw. Tap-Punkt; bei Tastatur (event.detail 0) Mitte des Bedienelements",
   "clip_wipe": "inset(0 0 100% 0) → inset(0 0 0 0), 420 ms, gleiche Kurve",
   "blende_ms": 200,
   "blende": "eingebaute Überblendung von ::view-transition-old/new(root), animation-duration 200ms, gleiche Kurve",
   "old_new_root": "animation: none; mix-blend-mode: normal (Kreis und Wipe)",
   "speicher": "localStorage 'seeweide-darstellung' = system | hell | dunkel (try/catch), Inline-Script im head vor dem ersten Paint",
   "tokens_hell": "Tag: --grund #e3e8e5, --karte #fbf6ee, --text #3e3a37, --akzent #2e5b57; Szene --s-himmel #d3dfe0, --s-see #a9bfc1, --s-fenster #8ea3a6",
   "tokens_dunkel": "Nacht: --grund #0c1317, --karte #152028, --text #ece5d7, --akzent #9cc6bc; Szene --s-himmel #0f1b24, --s-see #0a151c, --s-fenster #f1c98c (Fenster und Spiegelungen leuchten)",
   "vorfuehr_zeiger": "transform 700 ms cubic-bezier(0.16, 1, 0.3, 1), Runde 9,9 s"
  },
  "ausloeser": "klick",
  "touch": "Identisch: der Kreis startet am Tap-Punkt. Größere Ziele (44 px) für Theme-Knopf, Sprachlink und Drei-Wege-Wahl, kein Hover-Hintergrund.",
  "reduziert": "Kein startViewTransition, Theme und Sprache wechseln sofort. Zusätzlich setzt eine prefers-reduced-motion-Regel alle ::view-transition-* auf animation: none.",
  "korrekturen": {
   "beschreibung": "Ein Klick auf den Theme-Schalter oder den Sprachlink merkt sich die Klickposition. document.startViewTransition() friert den alten Zustand als Bild ein, dann wird umgeschaltet. Der neue Zustand wird per clip-path: circle() vom Klickpunkt aus aufgezogen, bis der Kreis die entfernteste Ecke erreicht. Der alte Zustand bleibt darunter stehen. Die Wahl wird gespeichert und vor dem ersten Paint angewendet, damit nichts aufblitzt.",
   "warum_es_wirkt": "Ein banaler Schalter wird zu einem kleinen Moment. Die neue Farbe kommt sichtbar vom Knopf, Ursache und Wirkung sind sofort klar.",
   "wann_einsetzen": "Portfolio- und Produktseiten mit gepflegtem Hell- und Dunkel-Design. Mehrsprachige Seiten, auf denen der Sprachwechsel ohne Neuladen passiert. Andere Modus-Wechsel, etwa ein Persona- oder Farbschema-Umschalter.",
   "wann_nicht": "Ohne sauberes Token-System für beide Themes, sonst entstehen Mischzustände. Wenn kein echtes Dark Theme gepflegt wird. Wenn der Wechsel sehr oft passiert, der Effekt nutzt sich ab. Bei sehr großen Seiten mit vielen Bildern kostet der Schnappschuss spürbar Zeit.",
   "varianten": [
    "Kreis vom Klickpunkt (Standard, 420 ms ease-out)",
    "Wipe von oben oder links über inset() statt circle()",
    "Kurze Überblendung (ca. 200 ms) für ruhige Seiten",
    "Drei Zustände System, Hell, Dunkel als Auswahl, etwa im Footer (Vercel)",
    "Sprachumschalter als dezenter Textlink in der Zielsprache statt Flaggen (Locomotive, Your Majesty)",
    "Light und Dark pro Komponente statt für die ganze Seite",
    "light-dark() mit Bildern (Chrome 150): ein Satz Farben und Bilder, umgeschaltet wird nur color-scheme"
   ],
   "umsetzung": "Beim Klick x und y aus clientX/clientY nehmen, bei Tastatur (event.detail 0) die Mitte des Knopfs. Radius r = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y)). Ohne document.startViewTransition oder bei reduzierter Bewegung direkt umschalten. Sonst const vt = document.startViewTransition(umschalten); vt.ready.then(() => document.documentElement.animate({ clipPath: [`circle(0px at ${x}px ${y}px)`, `circle(${r}px at ${x}px ${y}px)`] }, { duration: 420, easing: 'cubic-bezier(0, 0, 0.58, 1)', pseudoElement: '::view-transition-new(root)' })). CSS: ::view-transition-old(root), ::view-transition-new(root) { animation: none; mix-blend-mode: normal; }. Themes nur über Tokens wechseln (data-theme am html) und Farben nicht transitionieren, sonst fotografiert die Transition einen Zwischenstand. Wahl in localStorage speichern (try/catch), color-scheme setzen und per Inline-Script im head vor dem ersten Paint anwenden. Das storage-Event gleicht andere Tabs ab. Auf einer echten mehrsprachigen Seite mit eigenen URLs braucht der Sprachwechsel Cross-document View Transitions (@view-transition { navigation: auto; }); der Kreis lässt sich dann über pageswap und pagereveal mit der gespeicherten Klickposition nachbauen.",
   "mobil": "Funktioniert identisch, der Kreis startet am Tap-Punkt. Ziele mindestens 44 px groß.",
   "reduzierte_bewegung": "Sofortwechsel ohne View Transition. Zusätzlich alle ::view-transition-* per prefers-reduced-motion auf animation: none setzen.",
   "barrierefreiheit": "Theme-Knopf mit Namen, der das Ziel nennt („Zu dunklem Design wechseln“). Beide Icons im Markup, das inaktive mit display: none. Immer eine Rückkehr zu „System“ anbieten, am besten als Radiogruppe. Sprachlink mit lang und hreflang, Sprachname in der eigenen Sprache („English“, „Deutsch“). Bei Tastaturbedienung startet der Kreis in der Mitte des Bedienelements."
  },
  "geprueft": "01.11.2026",
  "notiz": "Demo: Startseite des erfundenen Boutique-Hotels „Hotel Seeweide, Lindau“ nach dem Rezept boutique-hotel-gastgeber (Newsreader opsz 72 für Headlines, Mona Sans breit für Labels, Seenebel-Grau statt Beige, Creme nur für Buchungsleiste und Kacheln, Seegrün als Hauston). Der Theme-Wechsel ist hier Tag und Nacht: eine eigene SVG-Szene (Bodensee, Alpen, Villa, Leuchtturm, Steg mit Badehaus) hängt komplett an den Tokens, nachts leuchten Fenster und Spiegelungen. Theme-Knopf, Sprachlink (DE/EN) und Drei-Wege-Wahl System/Tag/Nacht im Fuß lösen den Übergang vom Klickpunkt aus. Buchungsleiste direkt im Hero, darunter Grundriss, Speisekarte und Anreise-Skizze als eigene Bildwelten. Umschalter Kreis, Wipe, Blende; die Werte-Zeile zeigt Radius und Ursprung des letzten Klicks. Die Vorführung klickt Theme, Sprache, Fuß-Wahl und Sprache in einer Schleife und speichert dabei nichts. Der Sprachwechsel ist hier same-document; mit echten Sprach-URLs braucht es Cross-document View Transitions. 01.11.2026 (AD): Markenname gegen einen erfundenen getauscht, um Verwechslung mit einer echten Marke zu vermeiden. Varianten 01.11.2026: Varianten nach Regel 13 (?v=tokens|light-dark, Gruppe „Variante“ in der Steuerung schreibt die URL; Kreis/Wipe/Blende bleiben eine Einstellung innerhalb jeder Variante). Neue Plattform-Variante light-dark() mit Bildern (Chrome 150) als Produktseite der erfundenen Software „Werkbuch“ (Rezept saas-produkt-praezise, Geist): Farben und drei Bildpaare (eigene SVG unter medien/theme-umschalter-view-transition-*) hängen nur an color-scheme. Fallback per @supports und Selektor am data-theme, die dritte Kachel zeigt, welcher Weg greift. Headless-Chromium 153 nutzt den nativen Weg; der Fallback wurde mit einer Testkopie ohne Unterstützung geprüft. Zugang 02.11.2026: --leise im hellen Thema .66 → .76 (4,8:1 auf Grund, 5,2:1 auf Karte; dunkel bleibt .62 mit 6,3:1), Speisekarte in der Restaurant-Kachel unter 360 px umbrechend, Kontrastmodus-Rahmen für Knöpfe.",
  "varianten": [
   {
    "id": "tokens",
    "name": "Zwei Token-Sätze (Hotel, Tag und Nacht)",
    "wann": "Seiten mit eigener Bildwelt aus Inline-SVG oder CSS, deren Farben komplett an Tokens hängen: Hotel, Gastronomie, Portfolio. Läuft in allen Browsern mit View Transitions, sonst Sofortwechsel.",
    "werte": {
     "umschalten": "data-theme=\"hell|dunkel\" am html, je Theme ein vollständiger Satz Custom Properties plus color-scheme",
     "bilder": "Inline-SVG-Szene mit fill=\"var(--s-…)\"; nachts leuchten Fenster über .nur-nacht (opacity var(--nacht))",
     "uebergang": "Kreis circle(0 → r at x y), Wipe inset(0 0 100% 0) → inset(0) oder Blende 200 ms; 420 ms cubic-bezier(0, 0, 0.58, 1)",
     "vorfuehrung": "Theme, Sprache, Fuß-Wahl, Sprache, Runde 9,9 s"
    }
   },
   {
    "id": "light-dark",
    "name": "light-dark() auch für Bilder",
    "wann": "Produkt- und Softwareseiten mit Screenshots oder Grafiken, die es hell und dunkel gibt: SaaS, App, Freelancer mit Produkt-UI. Ein Satz Farben, kein zweiter Token-Block, „System“ folgt dem Betriebssystem ohne Skript.",
    "werte": {
     "umschalten": "nur color-scheme am html (light | dark; bei „System“ light dark)",
     "farben": "einmal als light-dark(): Grund light-dark(#f6f7f9, #08090b), Fläche light-dark(#ffffff, #121418), Text light-dark(#14161a, #eceef2), Akzent light-dark(#3346d3, #8f9cff)",
     "bilder": "background-image: light-dark(url(…-hell.svg), url(…-dunkel.svg)) für Produktansicht, Bericht und Telefon",
     "weiche": "@supports (background-image: light-dark(url(\"data:,a\"), url(\"data:,b\"))); davor als Fallback :root[data-theme=\"dunkel\"] .p-app { background-image: url(…-dunkel.svg) }. Im Skript CSS.supports(…) für die Anzeige",
     "browser": "light-dark() mit Bildwerten (url(), image-set(), none) ab Chrome 150 (stabil 30.06.2026), laut Chrome-Release-Notes wie in Firefox bereits umgesetzt; für background-image, list-style-image, border-image-source, cursor, content. Ältere Browser: Weiche, sichtbar gleich",
     "vorladen": "alle sechs Bilder beim Wechsel in die Variante per new Image(), damit der View-Transition-Schnappschuss nie ein leeres Bild zeigt",
     "schrift": "Geist 400/500/600, h1 clamp(2.5rem, 1.6rem + 3.6vw, 4.5rem), -0.022em; UI 150 ms cubic-bezier(0.25, 0.46, 0.45, 0.94)",
     "uebergang": "wie Grundvariante (Kreis, Wipe oder Blende)",
     "vorfuehrung": "Theme-Knopf zweimal, dazwischen Blick aufs Produktbild, Runde 7,4 s"
    }
   }
  ]
 }
}
