VideoGaga 同时支持明暗主题,但首页保持固定的深色视觉。随着页面数量增加,主题问题先后出现在服务端渲染、项目页、工作流和输入框占位文本中。
第一个问题:服务端不知道浏览器偏好
早期主题初始化会读取 localStorage 和系统颜色偏好。浏览器可以访问这些信息,服务端不行,于是服务端输出的主题可能与客户端第一次渲染不同,产生 hydration mismatch。
提交 886834b 将初始化拆成两个阶段:
- 服务端和客户端第一次渲染都使用确定的深色默认值;
- 组件挂载后,再在 effect 中读取本地偏好并更新主题。
它牺牲了“首帧立即命中用户偏好”,换取服务端与客户端初始结构一致。对于 SSR 页面,确定性通常比在初始化函数里读取客户端环境更重要。
第二个问题:组件绕过了主题系统
hydration 修好以后,项目页和工作流仍然存在明暗主题不一致。原因不是主题上下文失效,而是组件直接写了 text-slate-*、text-black 等固定颜色。
提交 ea0be24 修复了项目页和 Step 1 的硬编码样式。随后 2ac3f88 把重复的 Tailwind 组合抽到全局 CSS,并建立了统一规则:主题页面使用共享的文本层级和 surface 变量,首页作为固定深色页面单独处理。
现在主题感知区域遵循几档语义层级:
- 主要文字;
- 次要文字;
- 帮助信息;
- 弱元数据;
- 输入框 placeholder。
组件表达的是信息层级,而不是自行决定浅色和深色下的具体颜色。
一致性来自约束,而不是补丁
主题问题很容易变成逐页修复:发现一个看不清的文字,就增加一个 dark: 类。这样短期有效,长期却会形成更多组合。
更稳定的方法是明确三层边界:
- Theme Provider 负责主题状态和 hydration;
- 全局 token 负责主题到颜色的映射;
- 页面组件只选择语义层级。
当规则能够被写进项目约定并用于代码审查时,主题才从一次 UI 修复变成可持续维护的系统能力。