为小程序搭一套设计令牌系统
FF14Search v5 做了一次比较彻底的 UI 重构,核心是用 CSS 变量令牌取代散乱的硬编码颜色和间距值。这篇文章记录这套系统的设计思路。
背景:重构前的混乱
重构前 uni.scss 里同时存在两套不同风格的变量:
// 旧系统:ff14 前缀,命名不统一
$ff14-primary: #22c55e;
--ff14-bg-color: #0b1220;
--ff14-card-bg: #131c2e;
$card-border-radius: 12px;各个页面的颜色和间距各自硬编码,同一个"卡片背景色"在不同文件里有三四个不同的写法。要改个主色调,要全局 grep 才能找完。
令牌系统设计
新系统全部使用 --ff-* CSS 变量前缀,与旧系统 --ff14-* 不冲突,迁移期可以并存。
背景层级
深色主题是 FF14Search 的默认主题,背景分五个层级:
| 变量 | 值 | 用途 |
|---|---|---|
--ff-bg | #0b1220 | 页面根背景 |
--ff-bg-2 | #0e1626 | 次级页面区块 |
--ff-surface | #131c2e | 卡片 / 行 / 面板 |
--ff-surface-2 | #1a2538 | 按压态 / 分段控件 |
--ff-surface-3 | #20304a | 更深层嵌套面 |
背景层级从深到浅(数字越大越浅),让视觉层次感一目了然。浅色主题下同一套变量对应不同的值,页面代码不需要改,切换主题只需替换 CSS 变量值。
文字层级
--ff-text: #f1f5f9; /* 主标题 / 主要内容 */
--ff-text-2: #cbd5e1; /* 次要内容 / 副标题 */
--ff-muted: #94a3b8; /* 元信息 / 图注 */
--ff-subtle: #64748b; /* 占位符 / 点状装饰 */四个层级基本覆盖了信息密度中等的界面所有文字场景,不需要更多了。
强调色
--ff-accent: #22c55e; /* 主题绿,所有强调元素 */
--ff-accent-bg: rgba(34,197,94,.12); /* 强调色背景(chip / badge) */
--ff-accent-ink: #062f12; /* 强调色上的文字(深色主题) */保留了 FF14Search 一贯的深色 slate + 绿色配色,避免游戏感太强,走 iOS 移动端风格。
语义色
--ff-danger: #ef4444; /* 错误 / 缺件 */
--ff-warn: #f59e0b; /* 溢出 / 超镶 / 提示 */
--ff-info: #0a84ff; /* 信息 / 链接 */
--ff-success: #22c55e; /* 同 accent */配装器里魔晶石超出上限会高亮 --ff-warn,属性缺件显示 --ff-danger,这几个颜色用得很频繁。
FF14 特有令牌
/* 物品稀有度 */
--ff-rarity-0: #8e8e93; /* 常规(灰) */
--ff-rarity-1: #22c55e; /* 稀有(绿) */
--ff-rarity-2: #0a84ff; /* 蓝品 */
--ff-rarity-3: #bf5af2; /* 紫品 */
--ff-rarity-4: #ff9f0a; /* 橙品 */
/* 职责色 */
--ff-role-tank: #0a84ff;
--ff-role-heal: #30d158;
--ff-role-melee: #ff453a;
--ff-role-range: #ff9f0a;
--ff-role-caster: #bf5af2;职责色在配装器的职业选择、广场的职业筛选标签里大量复用,统一定义后不用在每个组件里各自维护。
原子类
令牌定义好之后,在 App.vue 的全局 <style> 里提供一套基于这些令牌的原子类,方便页面直接用 class 而不是写内联样式:
/* 颜色原子类 */
.ff-text { color: var(--ff-text); }
.ff-text-2 { color: var(--ff-text-2); }
.ff-muted { color: var(--ff-muted); }
.ff-accent { color: var(--ff-accent); }
/* 背景原子类 */
.ff-surface { background: var(--ff-surface); }
.ff-surface-2 { background: var(--ff-surface-2); }
/* 间距原子类 */
.ff-p-sm { padding: var(--ff-space-2); } /* 8px */
.ff-p-md { padding: var(--ff-space-3); } /* 12px */
.ff-p-lg { padding: var(--ff-space-4); } /* 16px */原子类不追求像 Tailwind 那样完备,只抽取高频组合,页面里仍然可以直接写 var(--ff-accent) 访问令牌。
文件职责划分
| 文件 | 职责 |
|---|---|
src/uni.scss | 全局样式入口,自动注入所有页面。定义 CSS 变量(令牌值)和 SCSS 静态量(边框圆角等) |
src/App.vue <style> | 全局原子类,基于 uni.scss 里的令牌,供所有页面直接使用 |
uni.scss 只定义变量,不写选择器样式;App.vue 只写原子类,不定义变量。两者各司其职,不互相污染。
迁移策略
旧系统(--ff14-*)在过渡期保留,新组件全用 --ff-*,存量组件按需迁移,不强制一次性重写。原则是"新代码不写旧变量,旧代码遇到就顺手改"。
这比一次性全局替换要安全得多——全局替换很容易漏掉边缘情况,分批迁移出问题了范围也小。
小结
这套令牌系统规模不大,但解决了一个实际问题:让设计变更有一个单一的修改入口。
改主色调,只改 --ff-accent;新增深色模式,只替换 CSS 变量值;稀有度颜色调整,只改 --ff-rarity-*。代码和组件不动。
令牌系统的核心价值不在于多复杂,而在于把"颜色是多少"和"颜色用在哪"这两个关注点分开管理。