主题引擎的字段是怎么定下来的
给一套 CMS 做主题系统,最难的不是渲染,而是「哪些东西该暴露给用户改」。这篇记录我把字段从 40 个砍到 20 个的取舍过程。
起点:什么都想让人改
第一版主题配置我列了 40 多个字段,从「卡片阴影 X 偏移」到「按钮 hover 时长」全都有。
结果是:没人会用。后台面板需要滚动三屏,改一个值不知道影响哪里,最后所有人都只用默认主题。
判断标准:一个字段该不该暴露
我最后用三个问题筛:
- 改它会不会让页面变丑? 会 → 不暴露
- 它能不能用「三档」表达? 能 → 做成枚举,不做连续滑块
- 站长会在什么场景下想改它? 说不出场景 → 删掉
按这三条,40 个字段砍到了 20 个左右。
几个具体的取舍
阴影偏移 → 三档强度
原本有 shadowX / shadowY / shadowBlur / shadowSpread / shadowColor 五个值。合并成一个 shadowIntensity: 'sm' | 'md' | 'lg',每档对应一整套预设好的数值。
理由:站长想要的是「轻一点/重一点」,不是「Y 偏移 2px」。
保留连续值的:模糊半径、透明度
毛玻璃的 cardBlur 和 cardOpacity 保留了 0-20 / 0-100 的滑块。因为这两个值的「手感」必须实时看,而且中间值确实有意义(8px 和 12px 差别明显)。
砍掉的:hover 时长、缓动曲线
没有人会为了「hover 从 200ms 改成 180ms」进后台。这类参数固定写死更专业。
命名踩过的一个坑
我很想把配置类型叫 ThemeConfig——直白、好记。但这个项目里 ThemeConfig 已经被「主页设置·主题与语言」占用了(presetId / mode / radius / fontScale / locale)。
如果硬复用这个名字,TypeScript 会在几十个文件里报错,而且语义完全混了。
最后改叫 ThemeFullConfig,并在类型定义里写了一条显式的警告注释,防止以后有人「顺手合并」这两个类型。
命名冲突不是靠记性避免的,是靠注释里的 🚨 和一句「为什么不能这么做」。
数据契约比 UI 重要
后台面板可以随时重做,但存进数据库的 JSON 结构一旦上线就很难改。
所以这套系统的做法是:
- 所有字段写入前必须过
validation.ts的 zod 校验 - 读取时再过一遍
coerceThemeConfig()清洗(宽松模式,非法值回落到默认而不是整个报错) - 每新增一组字段,同步补 zod schema + 清洗函数 + 默认值 + 单元验证
四件套齐了才算完成,少一件就会在某个边缘情况崩掉。