Communitygithub.com

sridhar-raj/blur-craft

在UI中实现高级毛玻璃效果并确保可读性:苹果级磨砂玻璃(具有厚度层级、发光边缘和着色层级的可测量材质)、现成玻璃表面(粘性导航栏、侧边栏/面板、底座、菜单、对话框、浮动提示、底部弹窗)、液体玻璃折射、渐进模糊、模糊揭示、发光与深度效果,并提供React、Vue、Svelte、Tailwind及原生(SwiftUI、Flutter、React Native、Compose)指导,以及对比度和霜冻检查工具。

blur-craft 是什么?

这是一个专注于UI玻璃效果的高级CSS/SVG技能,旨在解决网络上大多数玻璃效果看起来廉价的问题——通常是因为背后缺乏有趣内容、材质只有单一着色和模糊、以及文本对比度未经测量。该技能提供了完整的苹果级磨砂玻璃实现方案(assets/glass.css),包含五种厚度层级(clear、thin、regular、thick、solid)、可调发光边缘角度和着色层级,以及现成的UI表面(粘性导航栏、底座、菜单、对话框、浮动提示等)。它支持React、Vue 3、Svelte 5组件封装,以及Tailwind v4、SwiftUI、Flutter、React Native和Compose原生指导。该技能还提供了两个关键检查脚本:frost-check.mjs(测量模糊去除的细节量)和glass-contrast.mjs(在最差背景下测量实际对比度,确保符合WCAG 4.5:1标准)。此外,它还包含液体玻璃折射(支持Chrome和WebKit)、扩展背景、渐进模糊、模糊桥接交叉淡入、焦点遮罩、玻璃形态与按压效果、合并玻璃形状、环境光晕以及色彩调配等高级效果。性能优化建议包括:避免同时模糊两层叠加表面、固定/粘性模糊每滚动帧重复计算、在低端设备上分层级降低玻璃效果(通过glass-tier.js基于设备内存、Save-Data、帧率探测和Compute Pressure自动降级)。该技能适合所有需要在UI中实现电影级质感同时保持无障碍可读性的前端开发者。

兼容平台~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/sridhar-raj/blur-craft/tree/HEAD

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

模糊工艺 (Blur craft)

大多数网络上的玻璃效果看起来很廉价,原因有三:背后没有什么有趣的东西,材质只有一层平坦的着色和一层模糊,以及上面的文字是没人测量过的混浊灰色。看起来昂贵的玻璃(苹果的材质、visionOS、最好的产品网站)按顺序做对了三件事:

  1. 玻璃背后的光需要精心设计。 玻璃会展示背后的东西,所以请在那里打光。
  2. 玻璃是一种材质,而不是一种着色。 饱和模糊、渐变填充、发光边缘、镜面光泽、深度、颗粒感。这就是 assets/glass.css。
  3. 文本需要被测量。 半透明玻璃会随着背后每个像素的变化而改变对比度,因此需要在最差的点上通过脚本、在三种状态下检查对比度(scripts/glass-contrast.mjs)。

快速路径

我正在构建…使用备注
粘性导航栏.gs-bar (surfaces.css)扩展背景;添加 gs-bar--on-scroll 使其仅在滚动后才变得玻璃化
覆盖在艺术图上的播放器、底座或药丸按钮.gs-dock.glass.glass--clear + liquid-glass.js清晰的液体玻璃,每屏一个;如果艺术图变化很大,则自适应色调
带文本的浮动卡片或面板.glass.glass--regular在细节丰富的照片上使用 thick
菜单、弹出菜单或命令栏.gs-menu.glass32px 覆盖约 86%,0.5px 顶部高亮
对话框.gs-veil + .gs-dialog.glass.glass--thick确认付款、删除或签名时使用 glass--solid;当玻璃镀铬层位于下方时使用 gs-veil--flat + .gs-modal-open
浮动提示或底部弹窗.gs-toast.glass.glass--regular / .gs-sheet.glass.glass--thickgs- 类仅用于布局;材质来自 .glass
内容在栏下淡出渐进模糊(配方 2)每个视图一个滚动边缘效果
标题出现或数值变化模糊揭示(4)/ 2px 交叉淡入(4b)每屏一次
页面被艺术图的颜色浸染艺术背景,烘焙(配方 8)绝不在固定玻璃下使用实时模糊
Tailwind v4 项目glass.css + glass.tailwind.css (glass-blur-24, …)Tailwind 自身的 backdrop-blur-* 在 Sonoma Safari 上会失效
原生应用 (SwiftUI, Flutter, React Native, Compose)references/native.md相同方法,每个工具包各自的材质
照片、视频或设计编辑器的镀铬层.glass.glass--thick.glass--neutral中性:不增加饱和度,玻璃不能改变作品的颜色

然后:设计光(§1),设置材质(§2),检查(§5)。assets/demo.html 实时展示了每个表面并带控制选项;打开它可以在选择之前看到每个类的效果。

1. 先设计光

确定每个玻璃表面正后方是什么,并让它成为那里最发光的东西。

  • 在玻璃背后放置一个光源: 一个光晕、艺术图中的太阳、产品的颜色、一个实时的 WebGL 组件。模糊的纯色仍然是纯色;虚空之上的玻璃只是一个灰色盒子。

  • 让光穿过边缘。 最好的玻璃一半覆盖在光上,一半覆盖在暗处,这样颜色会沿着它的宽度变化。

  • 将边缘朝向光源。 将 --glass-light 设置为光的颜色(日落时为 255 200 160),将 --glass-rim-angle 设置为它的方向。边缘的亮端面向光源:

    光源来自左上上右上右右下下左下左
    角度135deg180deg225deg270deg315deg0deg45deg90deg

    位于玻璃下方的光源,其角度取最接近边缘的角度加上约 15deg 朝向中心。该角度是一个注册的 @property,因此它可以跟随指针或滚动平滑移动(苹果的高光会随着光移动而在材质周围移动)。

  • 将玻璃的尺寸做得像主角。 当它足够大以显示其下的场景变化时,它才会被解读为一种材质。苹果使更大的玻璃表现为更厚的玻璃:更深的阴影,更柔和的散射。

2. 设置材质

使用 assets/glass.css(复制时重命名 glass 前缀)和 assets/surfaces.css 来获得现成的表面,或者使用组件:Glass.tsx (React)、Glass.vue (Vue 3)、Glass.svelte (Svelte 5)。不要手写一行的 backdrop-filter 卡片。

使用字面量值编写 backdrop-filter,绝不用 var(),无前缀的声明放在最前,-webkit- 前缀的放在最后。 macOS Sonoma 上的 Safari 18 和 26 在使用自定义属性构建滤镜时不渲染模糊效果(WebKit 问题 297620,未修复),而 Tailwind v4 的 backdrop-blur-* 正是这样构建的。同一问题报告显示,即使使用字面量值,那里的无前缀属性也会失效,因此带前缀的声明放在最后,Safari 会应用它(Chrome 和 Firefox 会忽略它)。glass.css 为每种厚度 × 色调提供了一个字面量滤镜,并通过类切换;变量适用于填充、尺寸和颜色。

厚度。 根据玻璃承载的文本量来选择。数值通过 demo.html 中的对比度检查器在明亮光源下进行校准:

类模糊, 暗化在明亮光源下通过 4.5:1?用途
glass--clear16px, 22%否:仅用于粗体或大号文本及图标底座、播放按钮、覆盖在媒体上的药丸按钮(苹果:清晰的玻璃需要粗体、明亮的内容和一个暗化层)
glass--thin24px, 30%在极亮光源下不通过控件、芯片、小卡片在中等背景下
glass--regular32px, 48%在校准页面上通过;播放器背后明亮的日落需要 thick 设置为 0.62播放器、面板、浮动提示:默认值。仅着色(模糊关闭)状态通常是第一个失败的
glass--thick44px, 56%是菜单、弹窗、对话框、任何文本密集的内容
glass--solid4px, 92%是支付、删除、签名(高于 ~85% 填充时大模糊无法被察觉,因此使用 4px)

添加 glass--light 用于浅色玻璃搭配墨水色文本,以及 glass--neutral 用于颜色关键型应用(照片、视频和设计编辑器):不增加饱和度或色调偏移,因为玻璃不能改变作品的外观。在细节丰富的照片上,从 thick 开始,使用 --glass-dim: rgb(10 10 18 / 0.65–0.75)(它是一个颜色,不是一个数字):生动的玻璃和可读的文本在此处相互冲突,因此将生动的部分用在边缘、光泽和玻璃周围溢出的光上。

两个层级,如同 iOS 26.1: 一个着色层级(更多不透明度,绝不更少)会在 prefers-contrast: more(Safari 用户可以访问的设置;Safari 没有 prefers-reduced-transparency)时启用,使用 glass--tinted,或通过 <html data-glass="tinted"> 全站启用。glass.css 也处理 Windows 高对比度模式(纯色 Canvas 表面)、打印(带边框的普通盒子)、屏幕截图工具的捕获模式,以及 RTL。

玻璃属于控制层(导航栏、工具栏、底座、菜单、弹窗、对话框)浮动在内容之上,从不属于内容本身,也绝不做玻璃叠玻璃(苹果,WWDC25)。仅对主要操作进行着色。匹配简报的基调:身临其境的简报采用强光和在主角控件上的清晰玻璃;信任型(金融、健康)则采用厚或实心玻璃,几乎不发光。

references/material.md 包含了其余内容:每个层的作用及原因、从 Apple.com、Linear、Raycast、Radix、Material 和 Fluent 读取的预设、苹果完整的 WWDC25 规则列表,以及基调表。

3. 标志性效果

references/recipes.md 中的代码:

  • 液体玻璃折射: 边缘像透镜一样弯曲其背后的东西,可选色差(0.6–1)。两个版本:liquid-glass.ts/.js 可以折射任何页面内容,但仅在 Chromium 中有效(Safari 不会为 SVG 背景滤镜绘制任何东西,WebKit 问题 245510);liquid-glass-webgl.js 折射页面自身绘制的背景(一个 <canvas>、<img> 或 <video>,比如一张英雄壁纸),在每个浏览器中都有效,已在 Chromium 和 WebKit 中验证渲染。在其他地方,磨砂材质必须看起来自成一格。折射和磨砂之间存在权衡:弯曲效果在清晰玻璃上可见(blur 0–6);磨砂效果(12–20)覆盖整个边缘到边缘,弯曲变成柔和的色彩偏移。清晰玻璃用于一个词或一个图标,磨砂玻璃用于句子,文本距离边缘一个边框宽度。绝不要在覆盖遮罩层的对话框上使用:那里已经没有清晰的东西可以弯曲了。
  • 粘性栏的扩展背景: 颜色在内容滚动到玻璃下方之前就进入玻璃。gs-bar--on-scroll 使栏在滚动后才变得玻璃化,仅使用 CSS(滚动驱动动画)。
  • 渐进模糊: 内容在栏下溶解而不是被剪切。堆叠的遮罩层是唯一的方法,直到 CSS 拥有 linear-blur()(已提议,csswg #13285)。
  • 模糊桥接交叉淡入: 状态切换时 2px 模糊(Emil Kowalski)。
  • 模糊揭示: 从 blur(8px) 开始,一次性,作为出现时刻。
  • 焦点遮罩: 对话框背后的页面后退 4–8px。
  • 玻璃形态与按压: 在附近玻璃形状之间的 layoutId 形态(光标:0.34s,[0.32, 0.72, 0, 1])和按压时的内发光脉冲,不是缩放。
  • 合并玻璃形状: 一个被 goo 滤镜 SVG 遮罩切割的玻璃层,使附近形状像苹果的玻璃容器一样用液态颈部连接(配方 11,已在 Chromium 中测试)。
  • 视频的环境光晕: 一个 10×6 的画布样本,取自正在播放的视频,放大(配方 8b)。
  • 着色: 结构玻璃的色度约 0.03–0.05,着色的相对颜色 alpha(配方 12)。
  • 景深、速度运动模糊、艺术背景(烘焙)、锁定内容、颗粒感。

4. 保持快速

先设计外观,然后坚持以下几点:

  • 长时间或循环动画作用于不透明度和变换,而不是模糊半径。短暂的一次性动画没问题:Apple.com 将其菜单帷幕的模糊从 0 动画到 20px,持续 320ms。
  • 绝不同时模糊两个叠加的表面(苹果的导航栏在弹出菜单打开时会降低自身的模糊)。当对话框在玻璃镀铬层上打开时,向 <html> 添加 .gs-modal-open(镀铬层停止模糊并变为接近不透明)并使用 gs-veil--flat,或者从图像或画布背景烘焙遮罩层。在照片编辑器测试中测量:一个实时的全屏遮罩层模糊覆盖五个玻璃面板,运行在 31fps;没有它,60fps。
  • 固定或粘性模糊每个滚动帧都会重新运行。每屏只用一个大的;在其下烘焙大尺寸模糊图像(测量:实时模糊 60 → 22fps,烘焙后回到 60fps)。
  • 手机:七个 18px 面板在 Mali-G57 上每帧约花费 3.5ms(75–80% 的帧按时完成 vs 没有时的 99%)。当面板超过几个时,在布局的手机断点处丢弃 backdrop-filter 并保留半透明填充(或者让 glass-tier.js 根据每台设备决定)。
  • 预算模糊区域,而不是半径或数量。 为此技能测量(Chromium,软件渲染 2×,移动背景,vsync 开启):半径 4 → 64px 和 1 → 9 个表面在相同总面积下没有区别;模糊屏幕的 90% 导致帧率降至 51fps(p95 33ms)。这与 Skia 下采样大模糊的行为匹配。数值是相对的(在快速的 Mac 上软件渲染),排序是通用的。
  • 给 html 一个不透明的背景色:这样 Safari 会跳过含玻璃页面的额外全屏合成层(WebKit 问题 271340:修复仅对不透明文档跳过该层)。
  • 不要在不透明填充下模糊。Framer 和 Dia 在纯色背景上提供了 backdrop-filter,支付了无人能看到的模糊成本。在 ~85% 填充以上,使用 ≤ 4px 或没有(Arc 的浮动提示:85% 和 3px)。
  • 按设备对玻璃效果分层:assets/glass-tier.js 根据设备内存、Save-Data、帧率探测(iOS 低电量模式故意将动画帧率限制在 30fps)和 Compute Pressure 设置 data-glass-tier 为全量、精简(一个 8px 模糊)或平坦(无模糊,更多着色),与 Android、Fluent 和 Apple 在弱电或节能设备上降低模糊的方式相同。
  • 在视频上,不要模糊正在播放的视频:将其采样到一个小画布中(配方 8b),并在全屏时放弃玻璃效果。Safari 绝不会将视频像素传递给 SVG 滤镜。
  • 背景根:一个父元素具有 opacity < 1、filter、遮罩、clip-path、mix-blend-mode、backdrop-filter 或上述任何属性的 will-change 会压平其内的玻璃。这包括 Framer Motion 父元素淡入和玻璃斑点父元素上的 goo filter。自行淡入玻璃;在完成后清除揭示滤镜(filter: none)。
  • backdrop-filter 使元素成为 position: fixed 子元素的包含块:将门户菜单、浮动提示和对话框移出玻璃容器。
  • 祖先上持续的 view-transition-name 会在 Chromium 中压平玻璃;仅在过渡期间设置名称。包装器上的框架淡入过渡也会产生相同效果:自行淡入玻璃本身。
  • 拍摄玻璃的屏幕截图、OG 图像或 PDF:html2canvas、Satori 和打印会丢弃或破坏模糊。在捕获期间在 <html> 上设置 data-glass-capture(glass.css 会使玻璃变为不透明),并查看 quirks.md。

5. 检查(必需)

在声明玻璃效果完成之前,运行所有这些检查:

  1. 霜冻检查: node <skill>/scripts/frost-check.mjs <url or file.html> 测量每个玻璃表面模糊移除的细节量(模糊开启 vs 关闭)。NO BLUR 意味着背景根、前缀错误或使用了 var(),或 Liquid Glass 映射过期;FLAT 意味着背后没有任何东西可以测量。若要手动检查,请将粗体红色文本放在玻璃背后,在 deviceScaleFactor: 1 下捕获并放大。
  2. 对比度,在最差背景下测量: node <skill>/scripts/glass-contrast.mjs <url or file.html>。请指向最明亮的现实状态,而不是漂亮的默认状态:最亮的照片、日光帧、推高 +2 EV 的图像、滚动到栏下的白色页面部分(添加 URL 参数或测试状态,以便脚本可以加载)。在一个编辑器中,默认照片以 8.7:1 通过,而一个明亮帧以 4.4:1 失败。也请检查手机宽度(--width 390 --height 844)。它会隐藏文本,找到每行文本下最亮和最暗的像素,并检查 4.5:1(大文本 3:1)在三种状态下:正常、仅着色(模糊关闭,如同 iOS 低电量模式显示的情况)和 prefers-contrast: more。通过增加一个厚度层级、提高 --glass-dim 中的 alpha 值(例如

…(原文截断)

相关技能