我把博客的样式系统推倒重写了一次
一次从"能跑就行"到"讲清楚为什么"的返工。记录了变量缺失、路由冲突、样式失效这些看起来互不相关却同源的问题。
技术
这篇不是教程,是一份返工记录。
起因
某天我发现改一行样式,全站十几页没反应。打开控制台一看,background-color 那里赫然躺着一个空值:
background-color: var(--su-card);
/* ^^^^^^^^^ 没定义 */
于是全站排查 var(--su-*) 的引用量——190 多处,而定义它的地方是 0 处。
为什么这么久都没发现
因为大部分页面根本没走这套体系。
项目里同时存在两套布局:一套顶栏的,一套侧栏的。顶栏那套自带完整令牌,风生水起;侧栏那套的所有样式都指向一批从未定义的变量。
于是就出现一个很微妙的状态:
- 你打开首页,一切正常
- 你打开某篇文章,一切正常
- 你打开侧栏布局的某个页面,卡片没了边框,背景透出底色,文字层级全部塌掉
而这些页面往往不是核心页面,不点进去根本看不到。
CSS 变量未定义时的行为
这是最值得记住的一点:
color: var(--su-foreground);
当 --su-foreground 未定义时,整条声明作废,不是“回退到继承”,而是直接被丢弃。所以 color 会走继承,而 background-color 会走初始值 transparent。
结果就是:看起来“有点淡”,而不是“明显坏掉”。这种半死不活的状态,比彻底崩掉更难查。
第二个坑:两个页面抢同一个路由
修完样式,构建通过了——然后日志里躺着一行警告:
[build] Could not render `` from route `/[...page]`
as it conflicts with higher priority route `/`.
原来首页被两个文件同时渲染:
index.astro渲染首页[...page].astro的第 1 页也是首页
而 [...page] 是 catch-all,优先级更低。谁优先不代表谁生效,只代表谁被静默丢弃——Astro 不会报错,只会警告。
更坑的是过滤条件:
// 有 bug:首页的 page 参数是空字符串,不是 "1"
return pages.filter((p) => p.params.page !== "1");
在 trailingSlash: "always" 配置下,第 1 页的 params.page 是 "",所以首页漏网了。
第三个坑:改内容时顺手改了样式
这个最难承认。
为了让几个页面“风格统一”,我重写了它们的模板。结果把友链页的申请表单、评论区的说明、注意事项条目全删了——因为我在重写时只关注了“视觉是否统一”,没关注“这个页面本来有什么功能”。
净删了 1000 多行,而我当时觉得“更清爽了”。
教训:统一风格应该改样式,不该重写结构。如果只需要换外观,改类名就够了;真要重写,先把原文件的完整内容读一遍。
现在的做法
- 令牌集中在单文件,颜色一律引用变量,不允许硬编码
- 构建后做产物校验——不看“编译通过”,而是去产物 HTML 里确认关键结构真的存在:
const ok = new RegExp(`${token}\\s*:`).test(cssHtml);
console.log(ok ? "OK" : "MISS", token);
- 改动前先看 diff 规模。删了 300 行却只为了“统一风格”,这本身就是危险信号:
git diff --stat
一点感想
技术债最麻烦的地方不在于它存在,而在于它看起来是正常的。
这个博客的样式问题藏了不知道多久,期间我还写了好几篇技术教程,教别人怎么配置 Cloudflare 和 EdgeOne。挺讽刺的——自己的站点 CSS 变量都没定义,却在教别人配 DNS。
不过也正是这件事让我想明白一个道理:越是“看起来能跑”的系统,越需要主动验证。构建不报错、页面能打开、点起来没毛病——这些都不构成“它是正确的”证据。
真正构成证据的是:你知道它为什么对。