Web Liquid Glass
一个用于网站的生产级玻璃材质,提取自一个真实的 Next.js + Tailwind 网站。完整的可运行源代码位于 reference/ 目录中:
reference/liquid-glass.css:整个材质(令牌、表面、变体、形状玻璃、折射、降级)。约 2200 行,包含详细注释解释每个值。按需阅读所需章节,而非整个文件。reference/glass.tsx:<Glass>React 组件和 SVG 折射滤镜。reference/glass-specular.tsx:指针和倾斜跟踪的镜面高光。
参考代码引入了两个站点辅助函数(cn 和来自 lite-mode 模块的 LITE_CLASS)。请替换为项目自身的 cn/clsx 以及用于“关闭效果”的类名。名为 sky-*、kiosk-* 以及祈祷相关的选择器是特定于站点的变体示例。可删除它们,或将其用作模式参考。
什么是玻璃
玻璃由四个部分组成。如果网页版只实现了其中第一个,看起来就会很廉价:
- 材质:模糊且饱和度提升(约 200%)。低于约 150% 时会呈现为灰色塑料。增加亮度提升和对比度降低,使背后的繁忙内容扩散为柔和的洗涤效果。
- 边缘:光捕获边缘处的一条明亮发丝线,对侧边缘较暗。没有边缘的模糊永远不会看起来像玻璃。
- 光泽:顶部边缘内侧的柔和镜面高光。
- 深度:一个宽大、非常柔和、低不透明度的阴影,而非紧凑的卡片阴影。
折射(内容在边缘处弯曲)是第五个部分,也是唯一一个网页无法低成本完成的部分。它需要 backdrop-filter: url(#svg-filter),仅在 Chromium 中有效。
玻璃放置位置
玻璃应放置在导航层(导航栏、胶囊导航、浮动控件、面板)上,而不是正文内容上。玻璃上的文字对比度取决于其背后滚动的内容,因此无法保证。保持不透明卡片作为内容的默认样式,并让玻璃成为每个表面的可选项(.lg)。
耗费实际时间的规则
- 玻璃 CSS 必须与
@tailwind base位于同一个文件中,并放在@layer内部。 Tailwind 按文件解析@layer,因此单独的 CSS 文件会在@layer base上报错。去掉包装器会使规则成为非分层规则,而非分层规则会覆盖所有 Tailwind 工具类。。lg { position: relative }会静默覆盖导航栏上的fixed。 - 永远不要写
--x: calc(var(--x) * 0.7)。 这是一个循环:在计算值时无效,它会静默回退到继承的值而不报错。请使用第二个变量(--lg-clarity-bump、--lg-press)。 - 一个玻璃表面只有一个边缘,并且由
::before绘制。 两个边缘(一个::before边缘加上内嵌box-shadow斜面,或边框盒外的扩展阴影)会产生“双边框”,任何 alpha 调整都无法修复。 corner-shape不会被继承。 当表面使用corner-shape: squircle时,每个在边界上绘制的伪元素都需要同时设置border-radius: inherit和corner-shape: inherit,否则边缘会沿着与主体不同的曲线走。- 不要对嵌套在玻璃内部的玻璃使用
backdrop-filter。 它会采样已经着色的背景,从而变得模糊不清。玻璃条内的选择芯片(.lg-chip)是一个没有自身模糊效果的着色透镜。 - 不要在
:root上动画化继承的自定义属性。 它会使整个文档的样式失效:在包含 60 个表面的页面上,每帧约 11.6 毫秒,而直接为每个元素写入相同值只需 2 毫秒。 - 导航栏在每个滚动帧上都会重新模糊。 给
.lg-bar一个较小的模糊半径并添加contain: paint。如果设备仍然卡顿,设置--lg-blur-bar: 0px;边缘和光泽承担了大部分视觉效果。 - 不要通过展平磨砂玻璃来修复故障。 一个近乎不透明的“纯色”导航栏会失去其外观。相反,应找到几何原因(遮蔽导致边缘处主体变薄,或重复的边缘)。
- SVG 折射透镜绝不能采样到自身盒子之外。 在 Chrome 中,那里会返回透明,原始内容会以鬼影形式显示出来。
Safari 和 iPhone
WebKit 支持 backdrop-filter,但拒绝在其内部使用 SVG 滤镜,因此 Safari 获得的效果少于 Chrome:永久无法实现折射。Safari 能够折射的是其自身的工具栏。设置 viewportFit: "cover" 使页面在其下方运行,然后在导航栏和页脚中强制使用 env(safe-area-inset-*)。
辅助功能和降级方案(务必始终发布)
每个表面都必须降级为一个近乎不透明的着色效果,看起来接近且每帧零开销:
@media (prefers-reduced-transparency: reduce)@supports not (backdrop-filter: blur(1px))@media (prefers-contrast: more):更强的着色和实心边缘- 一个根类,例如
.lg-off,用于一次性关闭所有玻璃表面(用于用户开关或低功耗模式)
为导航栏单独设置更高的最大着色值(--lg-tint-max-bar),以便当内容在其下方滚动时,导航链接能保持文字对比度。
如何应用
- 将
reference/liquid-glass.css中的令牌块和表面部分复制到项目的主 CSS 文件中,放在其现有的@layer块内部(规则 1)。 - 将颜色令牌映射到项目的调色板。参考代码使用了 shadcn 风格的
hsl(var(--card))令牌。 - 使用来自
reference/glass.tsx的<Glass>组件,或直接使用.lg、.lg-bar、.lg-nav、.lg-pane、.lg-chip和.lg-refract类。 - 仅在其值得开销的地方添加来自
reference/glass-specular.tsx的镜面高光(如主界面控件、主导航栏),并尊重prefers-reduced-motion。 - 在 Chrome 和 Safari 中测试,分别在亮色和暗色模式下,在繁忙的照片和纯文本上方测试。
现成控件:Glisse
Glisse 由 Kelvin Zhang 开发(npm install glisse,MIT 许可,React 18+,无其他依赖),提供了内置按压提升透镜行为的交互式液态玻璃控件:Button、Switch、Slider、TabBar、SegmentedControl、Menu、Input、SearchField 以及一个纯 Glass 表面。其样式由 JS 包注入,因此无需导入 CSS 文件。
何时使用哪一种:
- Glisse 用于控件:开关、滑块、标签栏和分段控件,其拇指在拖动时提升为透镜。手工构建这些需要数天时间。
- 本材质 用于表面:导航栏、胶囊导航、面板、面板、表格、照片上的卡片,以及任何需要匹配现有设计系统或 Tailwind 主题的内容。
组合使用时的陷阱:
- 两者都使用
lg-前缀。 Glisse 定义了.lg-glass、.lg-button、.lg-tab-bar以及变量--lg-blur、--lg-tint、--lg-rim和--lg-filter,这些与本材质的令牌冲突。在同时使用两者的页面上,请重命名本材质的前缀(例如改为ylg-),或将 Glisse 限定在重置这些变量的包装器内。否则,两者会静默地相互调整。 - Glisse 的暗色模式基于
data-theme="dark"或prefers-color-scheme,而非 Tailwind 的.dark类。使用基于类的暗色模式时,请在<html>上同时设置data-theme和.dark,否则 Glisse 会保持亮色。 - Glisse 处理
prefers-reduced-motion,但不处理prefers-reduced-transparency或prefers-contrast。 请在 Glisse 控件周围添加本技能辅助功能章节中的降级方案。 - Glisse 的
.lg-glass已经通过内嵌box-shadow绘制了一个边缘。 不要在其上添加::before边缘(规则 3:只能有一个边缘)。