liquid-glass
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseLiquid Glass
Liquid Glass
Reusable refraction glass for . Full docs:
— read it
before implementing anything non-trivial.
components/grid-wallet-democomponents/grid-wallet-demo/src/components/liquid-glass/README.md可复用的折射玻璃,用于 。完整文档:
—— 在实现任何非琐碎功能之前请先阅读。
components/grid-wallet-democomponents/grid-wallet-demo/src/components/liquid-glass/README.mdThe rule that prevents 90% of the pain
能避免 90% 痛苦的规则
The glass refracts its own CHILDREN, not the page behind it. There is no
"frost whatever is behind me" mode ( can't refract portably). To
bend a background you put a copy of it inside the glass. So:
backdrop-filter- Glass over a known backdrop (color / gradient / image / pattern) → use
, which makes the copy for you.
GlassOver - Glass over live UI behind a small surface (over a static screen) → achievable: feed the glass a positioned copy of that UI. See "Refract live UI behind a small surface" in the gotchas / README.
- Glass over arbitrary scrolling/animating DOM → not achievable. Tell the user.
Exception: a big frosted surface (sheet/modal) should NOT refract — the
per-frame SVG filter tanks Safari. Use (GPU + a
specular edge). Refraction is for the small, tactile elements.
FrostPanelbackdrop-filterSkipping this gives a flat panel that doesn't lens (the classic failure).
玻璃折射的是它自己的子元素(CHILDREN),而不是其背后的页面。 不存在
“把我后面的东西磨砂”的模式( 无法可移植地折射)。要
弯曲背景,你需要把背景的副本放进玻璃内部。因此:
backdrop-filter- 玻璃放在已知背景(纯色 / 渐变 / 图片 / 图案)上 → 使用
,它会为你生成副本。
GlassOver - 玻璃放在小表面背后的实时 UI 上(覆盖在静态屏幕上)→ 可实现:把该 UI 的一个定位副本喂给玻璃。参见 gotchas / README 中的 “折射小表面背后的实时 UI”。
- 玻璃放在任意的滚动/动画 DOM 上 → 不可实现。请告知用户。
例外:大尺寸磨砂表面(sheet/modal)不应折射——逐帧 SVG 滤镜会拖垮
Safari。使用 (GPU + 高光边缘)。折射用于小而
有触感的元素。
FrostPanelbackdrop-filter跳过这一原则会得到一个没有透镜效果的平面面板(经典失败)。
Pick the right entry point
选择合适的入口
| Need | Use |
|---|---|
| Glass button / tab bar / card / chip over a known bg | |
| Refract content you already render, or custom backdrop wiring | |
| A big frosted sheet/modal (no refraction — fast on Safari) | |
| A small surface that refracts the (static) screen behind it | |
| A lens over the LIVE aurora that must work on Safari | WebGL: |
| The phone preview's WebGL stage | |
SVG vs shader (which renderer): default to the SVG path ( /
) for anything made of live DOM — buttons, tab bars, cards, text,
layouts (keeps content selectable/clickable). The shader / WebGL path is only
needed when you're refracting content that is not live DOM — a
drawing (e.g. a generated QR) or a (Safari won't SVG-filter those).
There is no auto-switching and no general "glass over a canvas/video" yet:
the only WebGL renderer () is bespoke to the phone and re-derives the
math in GLSL. If a canvas/video glass actually comes up, generalize
(or build a map-fed WebGL glass) on demand — don't assume a drop-in exists.
GlassGlassOver<canvas><video>StageGLStageGL| 需求 | 使用 |
|---|---|
| 已知背景上的玻璃按钮 / 标签栏 / 卡片 / chip | |
| 折射你已渲染的内容,或自定义背景接线 | |
| 大尺寸磨砂 sheet/modal(无折射,Safari 上很快) | |
| 折射背后(静态)屏幕的小表面 | |
| 必须兼容 Safari 的实时 aurora 透镜 | WebGL: |
| 手机预览的 WebGL 舞台 | |
SVG vs shader(选用哪个渲染器): 对于任何由实时 DOM 构成的内容——按钮、标签栏、卡片、文本、布局——默认走 SVG 路径( / ),这样能保持内容可选中/可点击。shader / WebGL 路径只在折射不是实时 DOM 的内容时需要: 绘图(如生成的 QR)或 (Safari 不会对这些做 SVG 滤镜)。目前没有自动切换,也没有通用的“canvas/video 上的玻璃”方案:唯一的 WebGL 渲染器()是手机专属定制,并在 GLSL 中重新推导数学。如果确实出现 canvas/video 玻璃需求,请按需泛化 (或构建一个由 map 驱动的 WebGL 玻璃)——不要假定已有现成方案。
GlassGlassOver<canvas><video>StageGLStageGLMinimal usage
最小用法
tsx
import { GlassOver } from '@/components/liquid-glass';
const BG = 'linear-gradient(135deg, #5b9dff, #b07cff)';
<div style={{ background: BG }}>
<GlassOver backdrop={BG} radius={16} depth={10} scale={18} cornerSmoothing={0.6}>
<span style={{ display: 'block', padding: '12px 22px', color: '#fff', fontWeight: 600 }}>
Settings
</span>
</GlassOver>
</div>backdropchildrenGlassConfigDEFAULT_GLASSPHONE_SHELL_GLASSFrostPanelFrostConfigGlassConfigtsx
import { GlassOver } from '@/components/liquid-glass';
const BG = 'linear-gradient(135deg, #5b9dff, #b07cff)';
<div style={{ background: BG }}>
<GlassOver backdrop={BG} radius={16} depth={10} scale={18} cornerSmoothing={0.6}>
<span style={{ display: 'block', padding: '12px 22px', color: '#fff', fontWeight: 600 }}>
Settings
</span>
</GlassOver>
</div>backdropchildrenGlassConfigDEFAULT_GLASSPHONE_SHELL_GLASSFrostPanelFrostConfigGlassConfigMust-know gotchas (details in the README)
必知的坑(详见 README)
- Hover/resize: animate , never width/height/radius — the displacement map re-bakes on shape change (jank).
transform: scale() - Refraction needs texture: over a flat color you only see specular/edge rim.
- Tiny elements: lower /
depthor they over-distort.scale - is directional (45° puts a bright highlight on the top-left/bottom-right corners) — rotate or lower
specularRotationto move it.edgeStrength - is Chromium-only (graceful circular fallback).
corner-shape: squircle - Outer drop shadow → round the wrapper. A glass surface clips itself with
, so an outer
overflow: hiddenhas to live on a wrapper around the glass (inside, the clip eats it).box-shadowtraces that wrapper's ownbox-shadow/border-radius, not the glass child's — so an unrounded wrapper gives a square halo around round glass. Give the shadow wrapper the samecorner-shape+border-radiusas the glass (seecorner-shape/GlassSymbolButtonGlassWindowButtonGroup). Same rule for any border/background painted on a wrapper. Debug heuristic: a square around glass = a rounding-unaware layer..root - Tiled backgrounds: pass 's
GlassOverto align.backdropOffset={{x,y}} - Circles & pills auto-lens radially (Aave's production circle/pill model,
detected from the geometry — no flag). doesn't apply to them; the dome applies to circles only. Don't fight it with per-axis expectations.
splay - Live/changing content inside a lens is a Safari minefield — stale
filter-output caching, composited descendants escaping the lens unfiltered
(/
translateZ(0)/motion'swill-changeleftovers), canvas/video killing the filter, and a LAYOUT-bounds source-size ceiling. Read "Safari field notes" in the README before attempting; the full experiment is parked onfilter: blur(0px).pat/scroll-refraction-experiment - WebKit renders SVG displacement over a copied subtree as EMPTY output at
notification size (confirmed June 2026 — see the README's "Confirmed dead
end" postmortem). Don't retry it: use the WebGL lenses for Safari refraction
and gate the SVG path to Chromium. Squircle-rect corner rules for Safari
(map smoothing with , overlay border-radius intersection, one-element frost, no drop-shadow wrappers) are in the same section.
cornerRadii - Verifying a bend over a soft backdrop: displacement is invisible without texture — draw a sharp debug grid through the bent coordinates to check.
- Big sheets/modals don't refract — they frost. A large displacement lens
re-runs its whole SVG filter every frame while the surface animates, which tanks
Safari (CPU ). Use
feDisplacementMap(GPUFrostPanel+ a static specular edge); keep refractivebackdrop-filter/Glassfor the small buttons on top. Example:GlassOver(FrostPanel) →apps/shared/BottomSheet.aurora/PasskeySheet - Refract live UI behind a small surface: pass a copy of the behind-UI as
's
GlassOver, anchored to align, and counter-animate it against any slide so it stays put while the glass slides over it. Behind-UI must be static while open. (Not for big sheets — see above.)backdropNode
- 悬停/缩放: 应使用 做动画,绝不要 animate width/height/radius——形状变化会导致位移图重新烘焙(卡顿)。
transform: scale() - 折射需要纹理: 在纯色背景上你只会看到高光/边缘轮廓。
- 微小元素: 调低 /
depth,否则会过度扭曲。scale - 是有方向性的(45° 会在左上/右下角产生明亮高光)——旋转或降低
specularRotation来移动它。edgeStrength - 仅 Chromium 支持(优雅回退为圆形)。
corner-shape: squircle - 外部投影 → 圆化 wrapper。 玻璃表面会用 裁剪自身,因此外部
overflow: hidden必须放在玻璃外部的 wrapper 上(放在内部会被裁剪吞掉)。box-shadow会跟随 wrapper 自身 的box-shadow/border-radius,而不是玻璃子元素——所以未圆角的 wrapper 会在圆玻璃周围形成方形光晕。给阴影 wrapper 设置与玻璃相同的corner-shape+border-radius(参见corner-shape/GlassSymbolButtonGlassWindowButtonGroup)。绘制在 wrapper 上的任何边框/背景同理。调试启发式:玻璃周围出现方形 = 某层不了解圆角。.root - 平铺背景: 传入 的
GlassOver来对齐。backdropOffset={{x,y}} - 圆形和胶囊形会自动径向透镜化(Aave 生产环境中的 circle/pill 模型,从几何形状自动检测——无需 flag)。不适用于它们;dome(穹顶效果)仅适用于圆形。不要用逐轴预期去对抗它。
splay - 透镜内的实时/变化内容是 Safari 雷区 —— 陈旧滤镜输出缓存、合成后代未经过滤地逃出透镜(/
translateZ(0)/motion 遗留的will-change)、canvas/video 杀掉滤镜,以及 LAYOUT-bounds 源大小上限。在尝试之前请阅读 README 中的“Safari 现场笔记”;完整实验停在filter: blur(0px)分支。pat/scroll-refraction-experiment - WebKit 会将复制子树上的 SVG displacement 渲染为空输出(通知尺寸下;2026 年 6 月确认——见 README 的“已确认死路”事后分析)。不要重试:Safari 折射使用 WebGL 透镜,并将 SVG 路径限制在 Chromium。同一节还包含 Safari 的 squircle-rect 圆角规则(用 做 map 平滑、overlay border-radius 交叉、单元素 frost、不用 drop-shadow wrapper)。
cornerRadii - 验证柔和背景上的弯曲: 没有纹理时位移不可见——用尖锐的调试网格穿过弯曲坐标来检查。
- 大尺寸 sheet/modal 不折射——它们磨砂。 大型位移透镜在表面动画时每帧重跑整个 SVG 滤镜,拖垮 Safari(CPU )。使用
feDisplacementMap(GPUFrostPanel+ 静态高光边缘);把折射backdrop-filter/Glass留给上面的小按钮。示例:GlassOver(FrostPanel)→apps/shared/BottomSheet。aurora/PasskeySheet - 折射小表面背后的实时 UI: 将背后 UI 的副本作为 的
GlassOver传入,锚定对齐,并反向动画它,使玻璃在滑动经过时它保持不动。打开期间背后 UI 必须是静态的。(不适用于大 sheet——见上文。)backdropNode
When tuning
调试时
There's a live tuning panel: run the demo (, port 4000), open the
App panel, switch it to swag mode. Sliders map 1:1 to .
npm run devGlassConfig有一个实时调试面板:运行 demo(,端口 4000),打开 App 面板,切换到 swag 模式。滑块与 一一对应。
npm run devGlassConfig