全部文章

3 分钟#design #css

毛玻璃也是一种纸:一次材质设计

Liquid Glass 用久了会冷。直到我把一层纸纹压进玻璃中央,才明白玻璃和纸不是两个阵营,而是同一个材质轴上的两端。

Liquid Glass 刚流行那阵子,我把所有卡片都换成了毛玻璃。

一周之后我把它改回来了。原因很简单:冷。

玻璃折射、高光边、饱和度拉满的背景,单看每一屏都很漂亮,连着读三篇东西就像在机场广告灯箱前面站了二十分钟。

后来我意识到问题不在玻璃本身,而在"玻璃背后什么都没有"。玻璃之所以像玻璃,是因为它压着东西。

一个念头:纸是被抽干的玻璃

某个凌晨我盯着磨砂玻璃杯看,突然想到:如果玻璃背后压着的是一张纸呢?

磨砂玻璃本来就是"不完全透明"的材料,它把背后的东西糊成一层薄薄的色块——这不正是被水浸润的纸吗?反过来,纸是抽干了水分的玻璃:不透明、不折射、但保留了纤维的肌理。

于是我把两者放到同一根轴上:

高含水量 ── 玻璃(透明·折射·水印纸纹 .04)
             │
             │  同一个材质轴
             │
低含水量 ── 纸(不透明·暖白·纸纹本体 .55)

这件事的好处立刻显现:入口页和文章页不再是两个网站,它们只是同一块材料的两端。

玻璃的三要素

要让一块面板真的读作"玻璃",而不是"贴了层灰膜的方框",必须同时具备三样东西。

1. 顶边高光

--specular: inset 0 1.5px 0 rgba(255,255,255,.85),
            inset 0 -1px 0 rgba(255,255,255,.14);

只有顶边一条 1.5px 的亮线,加底边一条 1px 的微反。少一条,面板立刻变平。

2. 背景折射

backdrop-filter: blur(18px) saturate(170%);

blur 决定"毛"的程度,saturate 决定它吸上来多少颜色。只模糊不增饱和,玻璃会显脏。

3. 中央纸纹

这是我自己加的那一笔。用 feTurbulence 生成一层噪点,铺在玻璃里,opacity: .04:

.glass::after {
  background-image: url("data:image/svg+xml,...");  /* 静态可平铺,不动画 */
  opacity: var(--grain-opacity);
  mix-blend-mode: var(--grain-mix);   /* 亮色 multiply / 暗色 screen */
}

它几乎看不见。但把 opacity 从 .04 调到 0 再对比,玻璃会立刻从"压着纸的磨砂玻璃"变回"一块塑料"。

纸的那一半

纸态反而更简单:不透明度拉满,纸纹浓度从 .04 提到 .55,投影全部去掉。

.paper {
  background: var(--bg);
  --grain-opacity: .55;
  box-shadow: none;   /* 纸不抬离页面 */
}

纸不投影是条硬规矩。加了阴影的纸就是卡片,不是纸。

两条性能红线

材质用得爽,但有两件事千万别做:

  1. 不动画 blur 半径。 每一帧都要重新做一次高斯模糊,中端手机上直接掉帧。
  2. 不动画纸纹。 feTurbulence 是 CPU 渲染的,动一下就是灾难。纸纹只能是静态的可平铺贴图。

所以我的圆斑只用 transform 漂移,纸纹一次生成、永不再动。

那个开关

材质轴需要一个显式开关,不然用户永远不知道"纸态"存在。

页头那枚 💧 / 📄 的药丸做的就是这件事:<html data-surface="glass|paper">,三值同拍过渡——--glass-bg、backdrop-filter、--grain-opacity——240ms。

入口页默认给你玻璃,文章页默认给你纸,但每一页你都可以亲手把它们调回去。

毛玻璃也是一种纸。玻璃是纸的含水态,纸是玻璃的干态,我们只是在同一个材质轴上滑动。

这句话后来被我写进了设计文档的第一行。

写于 2026 年 10 月 29 日 · 全文完

回到顶部