FROM THE NOTEBOOK
主题不是皮肤:让配色成为多层渲染的共同数据
用语义 CSS 令牌同时驱动 DOM、背景 Canvas 与文章私有组件。

主题切换最容易退化成一组散落的颜色覆盖。组件知道“这个地方是蓝色”,Canvas 又维护另一套蓝色,最终每增加一个主题都要修改多个系统。
从颜色名转向语义
组件依赖的是 background、surface、text、muted、signal 与 accent,而不是某个主题下的具体十六进制颜色。
:root[data-theme='abyss'] { --bg-rgb: 9, 17, 21; --text-rgb: 230, 235, 239; --signal-rgb: 92, 205, 183;}背景也读取同一份数据
页面背景 Canvas 在绘制时通过 getComputedStyle(document.documentElement) 读取 signal、accent 和 text。切换配色会广播一个小事件,背景只重绘一次,不运行持续动画。
这样背景仍然具有视觉层次,却不会拿走正文的主线程预算或阅读注意力。
只留下两套真正不同的主题
本站最终保留 mist 与 abyss。它们不是亮暗模式的机械反相:mist 以纸张、矿物蓝和低饱和暖色组织白昼阅读;abyss 用近黑底、冷青信号色和更克制的面积亮色组织夜间阅读。主题数量减少之后,每一套都能获得完整的组件、图像蒙版、代码高亮与 Canvas 校准。
色彩分布比色值更重要
大面积背景和正文只承担稳定阅读,强调色只进入交互节点、主题识别与少量数据。卡片不再统一铺一层“AI 紫色渐变”,而是按内容类型分配不同的边缘色、封面滤镜和局部纹理。颜色因此能说明层级,不只是提高饱和度。
建立可执行的主题合同
新增组件不得直接依赖 #rrggbb;Canvas、DOM 与文章私有界面都从相同的语义令牌取值。构建检查负责阻止第三套主题进入内容数据,视觉回归则在 mist、abyss、窄屏和减少动态效果四种条件下抽样。配色一旦成为合同,风格才不会随着页面增长重新碎裂。
环境主题与内容强调色必须分层
项目最初把“暗色页面”“工程文章的蓝色”“危险状态的红色”混在同一套变量里。结果是切换环境主题会改变内容身份,新增主题又需要复制所有组件状态。现在环境层只回答背景、表面、正文、边界和环境光如何变化;内容层由 aqua、coral、violet、gold 四种稳定 accent 回答当前主题属于哪条阅读路径。
文章通过第一主题关系取得 accent,而不是按列表奇偶数轮换。这样同一篇文章从首页移动到归档、相关文章或搜索结果时仍保持色彩身份。辅助色由显式关系表配对,缺失或未知值回落到安全默认值,避免过去那类 undefined is not iterable 在首页聚合时直接中断渲染。
RGB 元组不是偶然的写法
令牌保存为逗号分隔 RGB 分量,使组件可以用 rgb(var(--signal-rgb)) 和 rgba(var(--signal-rgb), .18) 共享同一颜色来源。它比同时维护实体色、半透明色、阴影色三组变量更不容易漂移,也让 Pixi 层能把计算后的浏览器颜色转换为自己的 tint。
代价是令牌值不能直接当作任意 CSS 颜色字符串消费,开发者必须经过约定的包装。这个限制被集中在视觉工具函数里,而不是让每个组件自己解析。设计系统的意义并不是“变量很多”,而是让错误的颜色使用方式更难发生。
色彩面积需要人工审查
自动检查能发现第三套主题、非法 accent 和硬编码的新增分支,却不能判断一张页面是否仍然灰成一片,也不能判断强调色是否已经覆盖正文。每轮视觉验收都要观察色彩面积:大表面保持安静,关键封面承担较大的内容色,按钮和焦点使用高对比信号,编号与证据使用较小暖色。只有把“在哪里用多少”纳入审查,语义令牌才不至于退化成另一份调色板清单。
本文采用 CC BY-NC-SA 4.0 许可;转载时请保留作者与原文链接。
