{
 "$schema": "../../schema.json",
 "schema": "fundus/1",
 "id": "liquid-glass-refraktion",
 "name": "Liquid Glass mit Refraktion",
 "name_en": "Liquid glass / refractive glass (SVG displacement)",
 "kurz": "Eine ziehbare Glaslinse bricht Titel und Bilder an ihrer Kante wie echtes Glas: SVG-Displacement-Map aus einem Squircle-Profil nach Snellius, als backdrop-filter, mit Rim-Light.",
 "kategorie": "Glas & Material",
 "kategorie_slug": "glas-material",
 "kategorie_nr": 10,
 "bau_chat": "C5",
 "prioritaet": "A",
 "trendstatus": "aufsteigend",
 "belegstatus": "belegt",
 "website_belege": 2,
 "stimmung": [
  "präzise",
  "luxuriös",
  "verspielt"
 ],
 "stimmung_roh": [
  "luxuriös",
  "technisch",
  "verspielt",
  "hochwertig"
 ],
 "technik": [
  "svg",
  "svg-filter",
  "backdrop-filter",
  "canvas",
  "vanilla-js",
  "webgl-shader"
 ],
 "technik_gruppen": [
  "css",
  "js",
  "webgl",
  "canvas"
 ],
 "leistungskosten": "mittel",
 "steckbrief": {
  "beschreibung": "Glas, das den Hintergrund an seiner Kante bricht wie eine echte Linse. Ein Höhenprofil beschreibt die Glaskante, z. B. ein konvexer Squircle y = (1 - (1 - x)^4)^(1/4). Aus der Steigung folgt per Snellius (Luft n = 1, Glas n = 1,5) die Ablenkung jedes Strahls. Die Verschiebungen werden für 127 Punkte auf dem Radius vorberechnet, um die Form gedreht und auf das Maximum normalisiert. Eine Displacement-Map kodiert sie als Farbe: R = 128 + x·127, G = 128 + y·127, B = 128. Ein SVG-Filter lädt die Map per feImage und verschiebt mit feDisplacementMap den Hintergrund. Ein zweites feImage legt ein Rim-Light (Lichtwinkel um -60°) per feBlend darüber.",
  "warum_es_wirkt": "Die Kante verbiegt, was darunter liegt, wie echtes Glas. Das liest sich sofort als hochwertig und neu, auch wegen der Nähe zu Apples Liquid Glass. Es unterscheidet sich klar vom flachen Milchglas.",
  "wann_einsetzen": "Für einzelne Hero-Elemente: Lupe über Bild, Such- oder CTA-Pill, Schalter, Player-Steuerung, schwebendes Navigations-Dock.",
  "wann_nicht": "Als Flächenstil für ganze Layouts. Wenn die Zielgruppe überwiegend Safari oder iOS nutzt und kein gutes Fallback da ist. Bei Formen, die ihre Größe laufend ändern, denn die Map muss für jede Größe neu gerechnet werden.",
  "varianten": [
   "Konvexe Linse mit Squircle-Profil",
   "Lip-Profil für Schalter: Rand erhaben, Mitte vertieft",
   "Chromatische Aberration: je Farbkanal ein leicht anderer scale",
   "Welle: fließendes Glasband über Bild oder Headline, die Rillen-Map wandert per feImage x",
   "Elastische Reaktion auf Ziehen, Hover oder Klick",
   "Lupe mit zweiter Map für die Vergrößerung",
   "WebGL-Shader auf Canvas-Hintergrund"
  ],
  "umsetzung": "Map einmal pro Größe in einem Offscreen-Canvas erzeugen und cachen. scale lässt sich ohne neue Map animieren, so blendet die Refraktion ein und aus. color-interpolation-filters=\"sRGB\" setzen, sonst stimmt die Verschiebung nicht. feImage muss exakt so groß sein wie das Element, backdrop-filter passt die Größe nicht an (filterUnits und primitiveUnits userSpaceOnUse, Breite und Höhe in px). In Chromium als backdrop-filter: url(#glas) anwenden. Für einen weichen Frost-Anteil vor der Verschiebung feGaussianBlur (z. B. 2,5px, edgeMode duplicate) einfügen. Safari und Firefox zeigen die Verschiebung nicht: per Feature-Erkennung auf Frosted Glass mit Kantenlicht zurückfallen.",
  "mobil": "iOS Safari kann keinen SVG-Filter in backdrop-filter: dort Frosted Glass als Fallback. Android Chrome kann es, die Map aber klein halten.",
  "reduzierte_bewegung": "Elastisches Nachfedern und animierten scale abschalten. Statische Refraktion ist unproblematisch.",
  "barrierefreiheit": "Die Refraktion verzerrt Text darunter, also nie über Fließtext legen. Inhalt im Glas braucht ausreichend Kontrast. prefers-reduced-transparency respektieren und dann einen deckenden Grund zeigen. Eine ziehbare Linse auch per Tastatur bedienbar machen."
 },
 "messwerte": {
  "easing": "Feder bei elastischer Variante (elasticity 0.15); cubic-bezier(.32,.72,0,1); Überblendung ease-in-out",
  "dauer_ms": "420 (Ebenenwechsel), 1100 (Frost ↔ Refraktion)",
  "stagger_ms": "",
  "distanz": "displacementScale 70 (Bibliotheks-Default); Vorblur 2.5px; Frost-Blur min. 4px",
  "sonstiges": "n Glas 1.5; 127 Strahlen pro Radius; Kanalmapping 0→-scale, 128→0, 255→+scale; Specular-Winkel -60°, Specular-Opacity 0.2–0.5; liquid-glass-react: blurAmount 0.0625, saturation 140, aberrationIntensity 2, cornerRadius 999; Sättigung 2.5 im Filter, Rand-Highlight per screen-Blend; Schatten hell 0 10px 30px #00000014, dunkel 0 18px 50px #00000080",
  "herkunft": "beobachtet",
  "quelle": "https://kube.io/blog/liquid-glass-css-svg/ ; https://github.com/rdev/liquid-glass-react; https://www.krea.ai/_app/f67fa903034ecddb/immutable/assets/portal.CQKncLZ0.css"
 },
 "kurve": {
  "bezier": [
   0.32,
   0.72,
   0.0,
   1.0
  ],
  "css": "cubic-bezier(0.32, 0.72, 0, 1)",
  "name": "eigene Kurve",
  "dauer_ms": 420
 },
 "gesehen_bei": [
  {
   "seite": "kube.io – Liquid Glass in the browser (Referenzartikel)",
   "url": "https://kube.io/blog/liquid-glass-css-svg/",
   "auszeichnung": "",
   "beobachtung": "Interaktive Komponenten (Lupe, Suchfeld, Schalter) mit physikalischem Refraktionsmodell, Profilen Convex/Squircle/Concave/Lip und Specular-Layer; Backdrop-Variante nur in Chrome.",
   "art": "artikel"
  },
  {
   "seite": "liquid-glass-react (Open-Source-Bibliothek)",
   "url": "https://github.com/rdev/liquid-glass-react",
   "auszeichnung": "",
   "beobachtung": "Kapselt Refraktion, Frosting, chromatische Aberration und elastisches Hover-Verhalten; Modi standard/polar/prominent/shader; Safari/Firefox zeigen die Verschiebung nicht.",
   "art": "demo"
  },
  {
   "seite": "Apple Liquid Glass",
   "url": "https://www.apple.com/newsroom/2025/06/apple-introduces-a-delightful-and-elegant-new-software-design/",
   "auszeichnung": "",
   "beobachtung": "Material reflektiert und bricht die Umgebung, reagiert mit Specular-Highlights auf Bewegung, Farbe aus dem Inhalt abgeleitet.",
   "art": "artikel"
  },
  {
   "seite": "KUBE (kube.io)",
   "url": "https://kube.io/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft 31.10.2026 (HTML/Bundle per curl abgerufen): Studio-Website des Referenzartikel-Autors: SVG-Filterkette feGaussianBlur → result blurred_source → feImage (vorberechnete Displacement-Map) → feDisplacementMap in=blurred_source in2=displacement_map; zusätzlich magnifying_displacement_map für Lupen-Glas; Regler „Progressive Blur Strength“ im Bundle.",
   "art": "website"
  },
  {
   "seite": "Krea",
   "url": "https://www.krea.ai/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft 31.10.2026 (HTML/Bundle per curl abgerufen): HTML: SVG-Filter #k3-glass = feGaussianBlur stdDeviation 2.5 (edgeMode duplicate) → feImage (Displacement-Map, per JS gesetzt) → feDisplacementMap in=frost in2=map (scale per JS) → feColorMatrix saturate 2.5 → feComponentTransfer (Tabelle 0/.3/.483/.64/.78) → zweites feImage 'rim' per feBlend screen. Prompt-Feld nutzt --glass-filter: url(#k3-glass). portal.CQKncLZ0.css: .liquid-glass mit Ebenen .frost (backdrop-filter blur(max(var(--glass-frost),4px)) saturate(1.6) contrast(.324) brightness(1.48)) und .refraction (backdrop-filter var(--glass-light)); Wechsel opacity .42s cubic-bezier(.32,.72,0,1), Überblendung frost↔refraction 1.1s ease-in-out; Dark-Mode eigene Werte (saturate 1.7, contrast .535, brightness 1.054).",
   "art": "website"
  }
 ],
 "tutorials": [
  {
   "titel": "Liquid Glass in the Browser: Refraction with CSS and SVG",
   "url": "https://kube.io/blog/liquid-glass-css-svg/"
  }
 ],
 "kombinierbar_mit": [
  "frosted-glass-erweitert",
  "karten-spotlight-glow",
  "html-in-canvas-shader"
 ],
 "urls": {
  "detail": "https://cecodesign.de/lab/fundus/glas-material/liquid-glass-refraktion/",
  "meta": "https://cecodesign.de/lab/fundus/glas-material/liquid-glass-refraktion/meta.json",
  "demo": "https://cecodesign.de/lab/fundus/glas-material/liquid-glass-refraktion/demo"
 },
 "herkunft": {
  "quelldateien": [
   "material3d.json",
   "patch-belege-2.json",
   "r3-belege-a.json"
  ],
  "pruefnotizen": [
   "patch-belege-2.json: Nur 1 Produktionsbeleg (Autor selbst). In ~100 geprüften Studio-/SaaS-Bundles sonst kein feDisplacementMap-Glas gefunden; Ziel ≥2 nicht erreicht, Prio-A wegen Trendlage belassen.",
   "r3-belege-a.json (Runde 3, 31.10.2026): Nur ein weiterer Produktionsbeleg gefunden. Weitere Treffer (Granola, La Revoltosa) nutzen feDisplacementMap nur mit feTurbulence für Handzeichnungs-Ränder, nicht als Glas."
  ]
 },
 "bau": {
  "status": "fertig",
  "kurz": "Eine ziehbare Glaslinse bricht Titel und Bilder an ihrer Kante wie echtes Glas: SVG-Displacement-Map aus einem Squircle-Profil nach Snellius, als backdrop-filter, mit Rim-Light.",
  "demo": true,
  "abhaengigkeiten": [],
  "werte": {
   "brechzahl": 1.5,
   "strahlen_pro_radius": 127,
   "profil_linse": "Squircle y = (1 - (1 - x)^4)^(1/4), Rand 40 % des Radius (46 px bei 230 px Linse)",
   "profil_lip": "squircle·(1 - s) + (1 - squircle)·s·0.6 mit s = smootherstep(x), Rand 72 % des Radius",
   "versatz": "tan(Einfall - Austritt) · (Höhe + 0.5), auf das Maximum normalisiert, Strahl knickt nach innen",
   "kodierung": "R = 128 - nx·m·127, G = 128 - ny·m·127, B = 128, A = 255; außerhalb neutral 128/128",
   "scale_linse": 70,
   "scale_prisma_rgb": "76 / 70 / 64 (je Kanal eine feDisplacementMap, per feColorMatrix getrennt, per feComposite arithmetic addiert)",
   "saettigung": "feColorMatrix saturate 1.4",
   "rim_light": "zweites feImage, weiß, Licht -60°, Kantenbreite 32 % des Rands, Deckkraft 0.5 (Lip 0.4, Dock 0.35), gegenüberliegende Seite 45 %, feBlend screen",
   "dock": "Pill 58 px hoch (Telefon 54 px), feGaussianBlur 2.5 edgeMode duplicate vor der Verschiebung, Rand 20 px, scale 48, Grund rgb(255 255 255 / .16)",
   "linse_groesse": "clamp(124px, 17vw, 230px), kreisrund",
   "filter": "filterUnits und primitiveUnits userSpaceOnUse, x 0 y 0, Breite/Höhe = Elementgröße in px, color-interpolation-filters sRGB",
   "einblenden_ms": 1100,
   "einblenden_kurve": "ease-in-out cubic-bezier(0.42, 0, 0.58, 1), scale 0 bis Zielwert",
   "variantenwechsel_ms": "420 aus, Map tauschen, 420 ein",
   "kurve": "cubic-bezier(0.32, 0.72, 0, 1)",
   "folgen_lerp": "0.28 pro 60-Hz-Bild",
   "elastizitaet": "0.15: Streckung 1 + min(0.22, Tempo · 0.15 · 0.05) in Laufrichtung, quer 1/Streckung, Rückfederung 0.18 pro Bild",
   "anheben": "scale(1.06) beim Greifen, Schatten 0 10px 30px #00000014 → 0 16px 44px #00000026 in 420 ms",
   "fallback": "Nicht-Chromium: backdrop-filter blur(4px) saturate(1.6), Grund rgb(255 255 255 / .14), Kantenlicht per inset-Schatten; prefers-reduced-transparency: rgb(246 244 239 / .94)",
   "ui_ms": 150
  },
  "ausloeser": "ziehen",
  "touch": "Linse mit dem Finger ziehen (touch-action: none, Pointer Events), Linse 124 px. Dock mit drei Einträgen, Umschalter mit größeren Tippflächen. Welle: das Band mit dem Finger nach oben oder unten ziehen.",
  "reduziert": "Statische Refraktion bleibt. Kein Lerp, keine elastische Streckung, kein Anheben, scale springt ohne Überblendung. Die Vorführung wechselt nur die Profile, die Linse bleibt stehen. Welle: Rillen stehen still, der Pause-Knopf entfällt.",
  "korrekturen": {
   "beschreibung": "Glas, das den Hintergrund an seiner Kante bricht wie eine echte Linse. Ein Höhenprofil beschreibt die Glaskante, z. B. ein konvexer Squircle y = (1 - (1 - x)^4)^(1/4). Aus der Steigung folgt per Snellius (Luft n = 1, Glas n = 1,5) die Ablenkung jedes Strahls. Die Verschiebungen werden für 127 Punkte auf dem Radius vorberechnet, um die Form gedreht und auf das Maximum normalisiert. Eine Displacement-Map kodiert sie als Farbe: R = 128 + x·127, G = 128 + y·127, B = 128. Ein SVG-Filter lädt die Map per feImage und verschiebt mit feDisplacementMap den Hintergrund. Ein zweites feImage legt ein Rim-Light (Lichtwinkel um -60°) per feBlend darüber.",
   "warum_es_wirkt": "Die Kante verbiegt, was darunter liegt, wie echtes Glas. Das liest sich sofort als hochwertig und neu, auch wegen der Nähe zu Apples Liquid Glass. Es unterscheidet sich klar vom flachen Milchglas.",
   "wann_einsetzen": "Für einzelne Hero-Elemente: Lupe über Bild, Such- oder CTA-Pill, Schalter, Player-Steuerung, schwebendes Navigations-Dock.",
   "wann_nicht": "Als Flächenstil für ganze Layouts. Wenn die Zielgruppe überwiegend Safari oder iOS nutzt und kein gutes Fallback da ist. Bei Formen, die ihre Größe laufend ändern, denn die Map muss für jede Größe neu gerechnet werden.",
   "varianten": [
    "Konvexe Linse mit Squircle-Profil",
    "Lip-Profil für Schalter: Rand erhaben, Mitte vertieft",
    "Chromatische Aberration: je Farbkanal ein leicht anderer scale",
    "Welle: fließendes Glasband über Bild oder Headline, die Rillen-Map wandert per feImage x",
    "Elastische Reaktion auf Ziehen, Hover oder Klick",
    "Lupe mit zweiter Map für die Vergrößerung",
    "WebGL-Shader auf Canvas-Hintergrund"
   ],
   "umsetzung": "Map einmal pro Größe in einem Offscreen-Canvas erzeugen und cachen. scale lässt sich ohne neue Map animieren, so blendet die Refraktion ein und aus. color-interpolation-filters=\"sRGB\" setzen, sonst stimmt die Verschiebung nicht. feImage muss exakt so groß sein wie das Element, backdrop-filter passt die Größe nicht an (filterUnits und primitiveUnits userSpaceOnUse, Breite und Höhe in px). In Chromium als backdrop-filter: url(#glas) anwenden. Für einen weichen Frost-Anteil vor der Verschiebung feGaussianBlur (z. B. 2,5px, edgeMode duplicate) einfügen. Safari und Firefox zeigen die Verschiebung nicht: per Feature-Erkennung auf Frosted Glass mit Kantenlicht zurückfallen.",
   "mobil": "iOS Safari kann keinen SVG-Filter in backdrop-filter: dort Frosted Glass als Fallback. Android Chrome kann es, die Map aber klein halten.",
   "reduzierte_bewegung": "Elastisches Nachfedern und animierten scale abschalten. Statische Refraktion ist unproblematisch.",
   "barrierefreiheit": "Die Refraktion verzerrt Text darunter, also nie über Fließtext legen. Inhalt im Glas braucht ausreichend Kontrast. prefers-reduced-transparency respektieren und dann einen deckenden Grund zeigen. Eine ziehbare Linse auch per Tastatur bedienbar machen."
  },
  "geprueft": "02.11.2026",
  "notiz": "Ausstellungsseite einer erfundenen Bonner Kunsthalle: riesiger Titel „Durchsicht“ und drei Arbeiten aus medien/, darüber eine ziehbare Glaslinse und ein Glas-Dock. Map und Rim-Light werden per Canvas aus dem Profil berechnet. Umschalter Linse, Lip, Prisma (chromatische Aberration). Werte-Anzeige zeigt die echten Parameter. Echte Refraktion nur in Chromium, sonst Frosted-Fallback mit Hinweis in der Werte-Anzeige. Vorführung: Linse wandert in einer Acht über Titel und Bilder, pro Runde (8 s) ein anderes Profil. Tastatur: Pfeiltasten verschieben die Linse um 24 px, mit Umschalt 72 px. Bilder 01.11.2026: eigene Bilder statt Portfolio. Zugang 02.11.2026: Titel ohne white-space: nowrap, mit overflow-wrap: anywhere (bleibt einzeilig, bei Textabständen 1.4.12 kein Querscrollen mehr); --leise Alpha .56 → .66 (5,2:1); Kontrastmodus-Rahmen für Knöpfe. Korrektur 02.11.2026: Variante welle (Prinzip „crystal waves“, eigener Code): Glasband über Titel und Bildern, Squircle-Rand plus periodische Rillen-Map, deren feImage-Ausschnitt per x wandert; Pause-Knopf, Frost-Fallback mit Lichtrillen. Umschalter Linse/Lip/Prisma/Welle als Varianten nach Regel 13 (?v=, replaceState); ohne ?v= wechselt die Vorführung Linse, Lip, Prisma, mit ?v= bleibt sie.",
  "varianten": [
   {
    "id": "linse",
    "name": "Linse: konvexes Squircle-Profil",
    "knopf": "Linse",
    "wann": "Eine ziehbare Lupe über Titel oder Bildern, etwa für Ausstellung, Kunsthalle oder Designstudio.",
    "werte": {
     "profil": "Squircle",
     "rand": "40 % des Radius",
     "scale": 70,
     "licht": 0.5
    }
   },
   {
    "id": "lip",
    "name": "Lip: Rand erhaben, Mitte vertieft",
    "knopf": "Lip",
    "wann": "Schalter, Regler und kleine Steuerflächen, die wie gedrückte Glasknöpfe wirken sollen.",
    "werte": {
     "profil": "lip",
     "rand": "72 % des Radius",
     "licht": 0.4
    }
   },
   {
    "id": "prisma",
    "name": "Prisma: chromatische Aberration",
    "knopf": "Prisma",
    "wann": "Experimentelle Kultur- und Kreativseiten, auf denen der Farbsaum an der Kante bewusst auffallen darf.",
    "werte": {
     "scale_rgb": "76 / 70 / 64"
    }
   },
   {
    "id": "welle",
    "name": "Welle: fließendes Glasband über der Headline",
    "knopf": "Welle",
    "wann": "Ausstellungs- oder Kampagnen-Hero mit großer Headline oder Bildreihe, über die ein Band aus geriffeltem Glas fließt (Experiment, nicht für Fließtext).",
    "werte": {
     "form": "Band calc(100vw − 2 × Rand) × clamp(84px, 10,5vw, 168px), Radius clamp(22px, 2,6vw, 40px), nur senkrecht ziehbar (Pfeiltasten hoch/runter)",
     "rand": "Squircle-Map mit 22 px Rand",
     "rillen": "zweite feImage-Map: h = sin(2πx/λ + 1,1 · sin(2πy/(1,3 · H))), R = 128 + 127 · cos, G = 128 + 127 · cos · cos(2πy/(1,3 · H)) · 0,45; λ 180 px, Map eine Periode breiter als das Band",
     "mischung": "feComposite arithmetic k2 1, k3 0,5, k4 −0,25 (Rand + 0,5 · (Rillen − 0,5))",
     "scale": 44,
     "fliessen": "feImage x läuft 0 → −λ mit 42 px/s und springt nahtlos zurück; hält bei Pause, verstecktem Tab und reduzierter Bewegung",
     "saettigung": 1.3,
     "licht": 0.45,
     "pause": "Knopf „Bewegung anhalten“ (aria-pressed) nur in dieser Variante",
     "fallback": "ohne Refraktion: Frost blur(4px) saturate(1.6) plus wandernde Lichtrillen (repeating-linear-gradient 150 px, 3,6 s linear)",
     "vorfuehrung": "Band gleitet in 8 s vom Titel über die Bilder und zurück"
    }
   }
  ]
 }
}
