VideoGaga 同时支持明暗主题,但首页保持固定的深色视觉。随着页面数量增加,主题问题先后出现在服务端渲染、项目页、工作流和输入框占位文本中。

第一个问题:服务端不知道浏览器偏好

早期主题初始化会读取 localStorage 和系统颜色偏好。浏览器可以访问这些信息,服务端不行,于是服务端输出的主题可能与客户端第一次渲染不同,产生 hydration mismatch。

提交 886834b 将初始化拆成两个阶段:

  1. 服务端和客户端第一次渲染都使用确定的深色默认值;
  2. 组件挂载后,再在 effect 中读取本地偏好并更新主题。

它牺牲了“首帧立即命中用户偏好”,换取服务端与客户端初始结构一致。对于 SSR 页面,确定性通常比在初始化函数里读取客户端环境更重要。

第二个问题:组件绕过了主题系统

hydration 修好以后,项目页和工作流仍然存在明暗主题不一致。原因不是主题上下文失效,而是组件直接写了 text-slate-*、text-black 等固定颜色。

提交 ea0be24 修复了项目页和 Step 1 的硬编码样式。随后 2ac3f88 把重复的 Tailwind 组合抽到全局 CSS,并建立了统一规则:主题页面使用共享的文本层级和 surface 变量,首页作为固定深色页面单独处理。

现在主题感知区域遵循几档语义层级:

  • 主要文字;
  • 次要文字;
  • 帮助信息;
  • 弱元数据;
  • 输入框 placeholder。

组件表达的是信息层级,而不是自行决定浅色和深色下的具体颜色。

一致性来自约束,而不是补丁

主题问题很容易变成逐页修复:发现一个看不清的文字,就增加一个 dark: 类。这样短期有效,长期却会形成更多组合。

更稳定的方法是明确三层边界:

  1. Theme Provider 负责主题状态和 hydration;
  2. 全局 token 负责主题到颜色的映射;
  3. 页面组件只选择语义层级。

当规则能够被写进项目约定并用于代码审查时,主题才从一次 UI 修复变成可持续维护的系统能力。