主题
Token 架构
设计 token 分 3 层 + 组件。从下到上依次抽象:纯色彩学 → 品牌身份 → 使用意图 → 组件消费。每一层只往上暴露稳定接口,下层换实现不影响上层。
┌──────────────────────────────────────────────────────────────────┐
│ 4 ATOM 组件 │
│ Button / Item / Badge / Navigation ... │
│ 只引用 SEMANTIC,从不直接引用 BRAND / PALETTE │
└──────────────────────────────────────────────────────────────────┘
↑
┌──────────────────────────────────────────────────────────────────┐
│ 3 SEMANTIC 语义层 · tokens/semantic/dark.json (74 token) │
│ "这个颜色用来做什么" │
│ text-default / text-accent / surface-elevated / │
│ state-hover / border-divider / hue-blue ... │
└──────────────────────────────────────────────────────────────────┘
↑
┌──────────────────────────────────────────────────────────────────┐
│ 2 BRAND ROLE 品牌角色 · tokens/brand/dark.json + brand.json │
│ "这个颜色代表什么品牌身份" │
│ brand-mark / brand-primary-* / brand-warning-* / brand-error-* │
└──────────────────────────────────────────────────────────────────┘
↑
┌──────────────────────────────────────────────────────────────────┐
│ 1 PALETTE 调色板 · tokens/core/color.hue.json + ... │
│ "这个颜色长啥样" — 纯色彩学,brand-agnostic │
│ lime / yellow / red / blue / purple / apricot ...11 hue │
│ nature N-1..N-5 / bg-light/dark/solid / overlay / tooltip │
└──────────────────────────────────────────────────────────────────┘Layer 1 · Palette 调色板
纯色彩学。 不带任何业务含义。位置:tokens/core/。
11 个 hue 色板
每个 hue 有 8 个 stop(4 solid + 4 alpha tint):
| Palette | 代表色(2 号位) | 用途线索 |
|---|---|---|
red | #EB5147 | 警示 / 删除 |
apricot | #F57D42 | 暖橙 |
orange | #F3A237 | 橙 |
yellow | #DABF22 | 提示 |
lime | #A7CD45 | 黄绿(当前 brand-primary) |
green | #4BC950 | 通过 / 在线 |
teal | #18CD91 | 青绿 |
blue | #79B5FA | 信息 / 链接 |
indigo | #8C96F9 | 靛 |
purple | #BB9AFB | 紫 |
magenta | #F586DC | 品红 |
每个 hue 的 CSS 变量:--mc-{name}-{stop},例:--mc-lime-2 --mc-red-t8。
完整调色板见 色彩 Color。
中性 / 背景 / 装饰
mc-n-1..mc-n-5:前景中性色(透明白阶 18% → 100%)mc-bg-light-1..4/mc-bg-dark-1..4:背景叠加(白蒙层 / 黑蒙层)mc-bg-solid-1..5:背景实色(页面 / 容器 / 浮层)mc-overlay-*/mc-tooltip/mc-border-light等:杂项
⚠️ palette 层名字不带任何业务暗示。lime 是黄绿色这个事实;它"恰好"是美餐当前的产品主色,但 palette 不知道这件事。
Layer 2 · Brand Role 品牌角色
业务身份。 把 palette 跟"品牌承担什么角色"绑起来。位置:tokens/brand/。
| Token | 当前映射 | 含义 |
|---|---|---|
mc-brand-mark | #6EB92C 直写 | 美餐 logo 标识色(不基于 palette) |
mc-brand-primary-* | → lime-* | 产品主色(链接 / 选中 / 强调) |
mc-brand-warning-* | → yellow-* | 警告 |
mc-brand-error-* | → red-* | 错误 / 删除 |
未来要加可加:mc-brand-info / mc-brand-success / mc-brand-neutral,按需。
为什么这一层存在
因为 palette 跟 brand 是两个概念,不该绑死。例子:
想把产品主色从 lime 换成 blue。
如果没有 brand 层,要把 atom 里所有 --mc-lime-2 改成 --mc-blue-2(几十处),而且 lime 这个名字也变得名不副实。
有 brand 层,只改 tokens/brand/dark.json 8 行:
diff
"primary": {
- "1": { "value": "{mc.lime.1}" },
- "2": { "value": "{mc.lime.2}" },
- "3": { "value": "{mc.lime.3}" },
- "4": { "value": "{mc.lime.4}" },
+ "1": { "value": "{mc.blue.1}" },
+ "2": { "value": "{mc.blue.2}" },
+ "3": { "value": "{mc.blue.3}" },
+ "4": { "value": "{mc.blue.4}" },
...
}pnpm build:tokens 一跑——所有组件主色变蓝,atom CSS 一行不改。
Layer 3 · Semantic 语义层
使用意图。 atom 描述"我在用啥"——背景、文字、状态、描边。位置:tokens/semantic/dark.json,74 个 token,7 组:
| 组 | 例子 | 用途 |
|---|---|---|
text-* | text-default text-accent text-warning-hover text-on-solid | 前景文字(含交互态 stepping) |
surface-* | surface-page surface-elevated surface-overlay | 容器底色(5 个 elevation 级别) |
solid-* | solid-accent solid-warning-pressed | 实色按钮背景(含 hover/pressed) |
state-* | state-hover state-pressed state-selected state-on-focus | 交互态叠加 |
btn-bg-* | btn-bg-background btn-bg-trans-hover | 按钮 type 的 bg 抽象 |
border-* | border-default border-divider border-focus | 描边 |
hue-* | hue-blue hue-purple hue-error | Badge / Tag 装饰色(12 色 + disabled) |
命名约定
mc-{layer-prefix}-{name}[-{state}]state后缀:-hover/-pressed/-disabled- 没 state 后缀 = default 态
- 大部分 atom 都 4 态全套,少数(如
hue-*)只有 1 态
atom 引用规范
✅ atom CSS 只用 semantic:
css
.mc-btn {
background: var(--mc-solid-accent);
color: var(--mc-text-on-solid);
}
.mc-btn:hover {
background: var(--mc-solid-accent-hover);
}❌ atom CSS 不要直接引用 palette / brand role:
css
/* DON'T */
.mc-btn { background: var(--mc-lime-3); }
.mc-btn { background: var(--mc-brand-primary-3); }如果发现某个用途 semantic 没覆盖,先回去加 semantic token,再回 atom 引用——不要走捷径绕过这层。
Layer 4 · Atom 组件
实际渲染层。CSS 引用 semantic,靠 props / state class 切换不同 semantic 变量。
例:Item.vue 里:
css
.mc-item--active-off { --mc-item-fg: var(--mc-text-default); }
.mc-item--active-green { --mc-item-fg: var(--mc-text-accent); }
.mc-item--active-white { --mc-item-fg: var(--mc-text-strong); font-weight: 500; }
.mc-item:hover { --mc-item-bg: var(--mc-state-hover); }active 三态对应三套 semantic 引用,hover 切 state 层——整个 atom 不知道 lime / red / brand-primary 是什么颜色。
切换 / 扩展 token 的标准流程
| 场景 | 改哪一层 |
|---|---|
| 换产品主色(lime → blue) | Brand Role(8 行 JSON) |
| 增加一个新装饰色(如 cyan) | Palette + Semantic.hue(两层都加) |
| 调整某个 atom 的 hover 配色 | Semantic(找对应 token) |
| 一个 atom 缺颜色用 | 先在 Semantic 加 token,再 在 atom 引用 |
| 切 light mode(v1.5) | 填 tokens/semantic/light.json(74 个 key 对到 light 等价)+ Style Dictionary 配置 mode 输出 |
| 加新组件类型(如 Tag) | 引用现有 semantic;缺什么补什么到 semantic 层 |
工具链
- Style Dictionary v5 把所有
tokens/**/*.json合并并输出build/css/tokens.css - 运行:
pnpm build:tokens(dev/build 前自动跑一次) - 配置:
style-dictionary.config.js(极简,所有 token 一起 dump) - Light mode 多 selector 输出留待 v1.5 配 SD 转换器
Token 总数(截至 2026-05-15)
| 层 | 文件 | token 数 |
|---|---|---|
| Palette | tokens/core/ 9 个 JSON | 约 130 |
| Brand Role | tokens/brand/ + tokens/brand.json | 25 |
| Semantic (dark) | tokens/semantic/dark.json | 74 |
| Semantic (light) | tokens/semantic/light.json | 0(待填) |
CSS 变量总输出:约 261 行。