Skip to content

为小程序搭一套设计令牌系统

FF14Search v5 做了一次比较彻底的 UI 重构,核心是用 CSS 变量令牌取代散乱的硬编码颜色和间距值。这篇文章记录这套系统的设计思路。


背景:重构前的混乱

重构前 uni.scss 里同时存在两套不同风格的变量:

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 变量值。

文字层级

css
--ff-text:    #f1f5f9;  /* 主标题 / 主要内容 */
--ff-text-2:  #cbd5e1;  /* 次要内容 / 副标题 */
--ff-muted:   #94a3b8;  /* 元信息 / 图注 */
--ff-subtle:  #64748b;  /* 占位符 / 点状装饰 */

四个层级基本覆盖了信息密度中等的界面所有文字场景,不需要更多了。

强调色

css
--ff-accent:     #22c55e;              /* 主题绿,所有强调元素 */
--ff-accent-bg:  rgba(34,197,94,.12);  /* 强调色背景(chip / badge) */
--ff-accent-ink: #062f12;              /* 强调色上的文字(深色主题) */

保留了 FF14Search 一贯的深色 slate + 绿色配色,避免游戏感太强,走 iOS 移动端风格。

语义色

css
--ff-danger:  #ef4444;  /* 错误 / 缺件 */
--ff-warn:    #f59e0b;  /* 溢出 / 超镶 / 提示 */
--ff-info:    #0a84ff;  /* 信息 / 链接 */
--ff-success: #22c55e;  /* 同 accent */

配装器里魔晶石超出上限会高亮 --ff-warn,属性缺件显示 --ff-danger,这几个颜色用得很频繁。

FF14 特有令牌

css
/* 物品稀有度 */
--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 而不是写内联样式:

css
/* 颜色原子类 */
.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-*。代码和组件不动。

令牌系统的核心价值不在于多复杂,而在于把"颜色是多少"和"颜色用在哪"这两个关注点分开管理。