全部文章

2 分钟#design #css

两套美工,不是两套皮肤

玻璃和纸不该共用一套变量。这一版把它们拆成两个对等的渲染程序:各写各的 token,各画各的层,切换只是换一个程序。

上一版里,纸是玻璃的「干态」:同一套 --glass-* 变量,[data-surface="paper"] 只是把数值改小、把模糊关掉。

能跑,但有个隐患——纸永远长在玻璃的骨架上。玻璃加一个新特性(比如跟着指针反光),纸就得跟着写一条「不要反光」的规则。久而久之,两套材质都变成了补丁。

这一版反过来了:它们是两个程序。

窗口只有骨架,材质是画上去的

<div class="window">
  <span class="window__backdrop"></span>  <!-- 材质层:折射 或 纸面 -->
  <span class="window__sheen"></span>     <!-- 反射层:只有玻璃有 -->
  <div class="window__content">…</div>    <!-- 内容 -->
</div>

骨架只负责几何:圆角、裁剪、过渡。材质层由 data-surface 选择用哪套程序来画:

liquidglass paper
材质层 壁纸副本 + SVG 位移场(真折射) 暖白底色 + 纤维纹理
反射层 指针驱动的 specular 热点 没有
纸纹 水印(.05) 本体(.07,暗色 .09)
变量前缀 --lg-* --pp-*

两者没有任何共享的材质变量。--lg-* 在纸态下不存在,--pp-* 在玻璃态下也不存在——不存在,就没人会误用。

为什么非要拆开

除了「概念干净」,还有一个很实际的原因:性能预算不一样。

  • 玻璃要算折射、要重绘、要 Worker,所以同屏最多两扇窗用真折射,其余退化成「色层 + 高光边」。
  • 纸什么都不用算,只是贴一张静态纹理。

如果共用一套变量,这种差异只能靠一堆 if 条件去表达;拆成两个程序之后,它是结构上的差异——玻璃实例登记进渲染器,纸实例压根不登记。

按钮也是窗口

一条小规矩

按钮不是「长得像玻璃的按钮」,它就是一个窗口实例:只指定位置与大小,宽度由文字决定。

<a class="window window--btn" data-window data-mode="soft" href="…">
  <span class="window__backdrop"></span>
  <span class="window__sheen"></span>
  <span class="window__content">读文章</span>
</a>

因为走的是同一套骨架与同一个材质程序,按钮的圆角、高光边、悬停位移和卡片永远一致——不是「对齐了」,是同源。

切换那一下

data-surface 一变,两个程序交替挂载:

  1. 玻璃的反射参数被清空(不再有人写 --sx/--sy);
  2. 纸程序给每扇窗分配一点纤维偏移,避免整页纹理重复;
  3. CSS 的 240ms 过渡负责「水分蒸发」。

骨架一点没动,内容一点没动。这就是「切换成本低」的真正含义——换的不是样式,是渲染程序。

代价

说实话:代码量比一套变量多了大约三成,第一次读的人得同时看两段 CSS。

但换来的是一句可以一直用下去的话:

想改纸,只改纸;想改玻璃,只改玻璃。互不牵连。

写于 2026 年 11 月 4 日 · 全文完

回到顶部