电商系统前后端样式治理规范(二)
·
五、前后端契约
5.1 后端职责清单
| 编号 | 职责 | 输出 | 约束 |
|---|---|---|---|
| B1 | 下发语义令牌集 | tokenSet + tokenSetDark |
只含值,不含 CSS 属性名 |
| B2 | 下发业务文案 | 按楼层/组件下发 | 文案与样式分离 |
| B3 | 下发图标/图片 URL | URL 字符串 | 不控制尺寸 |
| B4 | 下发 A/B 实验标识 | 实验组 ID | 不直接下发样式,前端根据标识选样式 |
| B5 | 下发间距/圆角刻度 | 刻度令牌 | 下发刻度名(如 spacing-md),不是像素值 |
| B6 | 保证令牌兜底值 | 每个 token 有默认值 | DUCC 异常时降级到默认 |
5.2 前端职责清单
| 编号 | 职责 | 实现 | 约束 |
|---|---|---|---|
| F1 | 建立 CSS 变量层 | :root { --token: value } |
所有令牌必须通过变量消费 |
| F2 | 实现主题切换 | @media (prefers-color-scheme) |
不依赖后端双配置键 |
| F3 | 自主控制布局 | Flexbox/Grid | 不从后端接收 margin/padding/height |
| F4 | 自主控制排版 | line-height/text-overflow | 不从后端接收排版规则 |
| F5 | 自主控制动画 | transition/animation | 不从后端接收动画参数 |
| F6 | 设备适配 | DPI 缩放/安全区域/响应式 | 后端不感知设备 |
| F7 | 令牌兜底 | var(--token, fallback) |
每个令牌使用处必须有兜底值 |
5.3 接口对比:当前 vs 规范
❌ 当前:CSS 属性级下发
{
"style": {
"color": "#FF0F23",
"fontSize": "14",
"fontWeight": "bold",
"margin": "0 0 0 2",
"height": "6",
"width": "6",
"lineHeight": "20",
"backgroundColor": "#FFF0F0",
"borderRadius": "4",
"padding": "4 8",
"textOverflow": "ellipsis",
"showLines": "2"
}
}
问题:后端知道 margin-left: 2px、text-overflow: ellipsis、height: 6px,这些是 CSS 规则,不是设计决策。
✅ 规范:令牌级下发
{
"tokens": {
"price-main-color": "#FF0F23",
"price-label-bg": "#FFF0F0",
"card-radius": "4px",
"font-size-label": "14px"
},
"tokensDark": {
"price-main-color": "#FF6B6B",
"price-label-bg": "#3A1A1A"
}
}
改进:后端只传递设计决策的值,前端自主决定如何用 CSS 实现。
六、暗黑模式规范
6.1 当前做法(配置键翻倍)
// 每种颜色两个配置键
text.color → 亮色
.text.dark.color → 暗色
title.color → 亮色
.dark.color → 暗色
// N 种颜色 × 2 = 2N 个配置键
问题:配置键线性膨胀,新增模式(如高对比度模式)继续翻倍。
6.2 规范做法(令牌集 + 前端主题)
// 后端下发两套令牌值,共用同一令牌名
tokenSet: { "price-main-color": "#FF0F23", "title-color": "#333333" }
tokenSetDark: { "price-main-color": "#FF6B6B", "title-color": "#EEEEEE" }
// N 种颜色 × 1 = N 个令牌名(值集可扩展)
前端消费:
:root {
--price-main-color: #FF0F23; /* JS 注入 tokenSet */
--title-color: #333333;
}
@media (prefers-color-scheme: dark) {
:root {
--price-main-color: #FF6B6B; /* JS 注入 tokenSetDark */
--title-color: #EEEEEE;
}
}
/* 新增高对比度模式 — 无需后端改动 */
@media (prefers-contrast: high) {
:root {
--price-main-color: #CC0000;
--title-color: #000000;
}
}
优势:
- 后端配置键数量 = 令牌数(不翻倍)
- 新增主题模式只需前端扩展,后端无需改动
- 令牌名稳定,前端代码不需要条件分支
七、反模式清单
7.1 十大反模式
| 编号 | 反模式 | 表现 | 危害 | 矫正 |
|---|---|---|---|---|
| AP-1 | 后端镜像 CSS | VO 字段与 CSS 属性 1:1 对应 | 违反关注点分离 | 用令牌替代 CSS 属性 |
| AP-2 | 后端硬编码像素值 | Java 代码中写死 height: "6" |
无法适配多 DPI 设备 | 像素值归前端 |
| AP-3 | 配置键翻倍 | 暗黑模式每种颜色两个键 | 维护成本 O(N×M) | 令牌集 + 前端主题 |
| AP-4 | 色值散落 | 多个 Java 类各自硬编码默认色值 | 设计规范变更时遗漏 | 统一到令牌系统 |
| AP-5 | 下发布局属性 | 后端下发 margin/padding/height | 设备差异无法处理 | 布局归前端 |
| AP-6 | 下发 CSS 规则 | 后端下发 textOverflow/fontFamily | CSS 规则不是设计决策 | 规则归前端 |
| AP-7 | 前端硬编码色值 | CSS 中写死 color: #FF0F23 |
无法动态调整 | 引用 CSS 变量 |
| AP-8 | 下发完整 CSS 字符串 | 后端返回 "color:red;font-size:14px" |
安全风险、难以校验 | 下发结构化令牌 |
| AP-9 | 暗黑模式简单反转 | 前端用 filter: invert(1) |
图片也反转,体验差 | 令牌级主题切换 |
| AP-10 | 配置键散落 | 字符串硬编码在业务代码中 | 难以搜索和维护 | 集中常量类管理 |
7.2 自检清单
面对一个样式属性,回答以下问题:
| # | 问题 | 是 → | 否 → |
|---|---|---|---|
| 1 | 运营是否需要不发版就修改该值? | 继续问 2 | 🟢 前端自主 |
| 2 | 修改该值是否改变 UI 语义(而非仅微调)? | 继续问 3 | 🟡 协商区 |
| 3 | 该值是否与设备/平台无关? | 继续问 4 | 🟢 前端自主 |
| 4 | 该值是"值"(色值/刻度/URL)还是"规"(CSS 规则)? | 值 → 🔴 后端下发 | 规 → 🟢 前端自主 |
| 5 | 后端下发的是令牌名+值,还是 CSS 属性名+值? | 令牌 → ✅ 合规 | CSS 属性 → ❌ 反模式 AP-1 |
八、实施路径
8.1 渐进式重构四阶段
8.2 Phase 1:剥离布局属性(1-2 周)
目标:从后端 VO 中移除布局属性,归还前端。
| 动作 | 具体内容 | 影响范围 |
|---|---|---|
移除 ComponentStyle 中的布局字段 |
margin, marginLeft, padding, height, width, lineHeight |
后端 VO 定义 + 前端消费 |
移除 ComponentStyle 中的 CSS 规则字段 |
textOverflow, showLines, maxLines, fontFamily, textDecoration |
后端 VO 定义 + 前端消费 |
移除 FloorCF 中的布局字段 |
height, marginTop, marginBottom(改为刻度令牌或前端自主) |
后端 VO + 前端消费 |
移除 BffComponentUtil 中的硬编码像素值 |
height: "6", width: "6", marginLeft: "0 0 0 2" |
工具类 + 前端消费 |
后端 VO 瘦身后:
// Phase 1 后:ComponentStyle 只保留设计决策
public class ComponentStyle {
private String color; // ✅ 色值是设计决策
private String fontSize; // 🟡 后续 Phase 2 改为刻度
private String fontWeight; // ✅ 字重是设计决策
private String backgroundColor; // ✅ 背景色是设计决策
private String borderRadius; // ✅ 圆角是设计决策
// ❌ 已移除:margin, marginLeft, padding, height, width
// ❌ 已移除:lineHeight, textOverflow, showLines, maxLines
// ❌ 已移除:fontFamily, textDecoration
}
8.3 Phase 2:语义化色值(2-4 周)
目标:将裸色值改为语义令牌,前端建立 CSS 变量层。
| 动作 | 具体内容 |
|---|---|
后端新增 TokenSetVO |
Map<String, String> 结构,key 为令牌名,value 为令牌值 |
后端在响应中增加 tokenSet / tokenSetDark 字段 |
与现有 style 字段并存,渐进切换 |
| 前端建立 CSS 变量注入层 | JS 解析 tokenSet,注入到 :root |
| 前端组件逐步迁移 | 从 style.color 改为 var(--price-main-color) |
新增 VO:
public class TokenSetVO {
private Map<String, String> tokenSet; // 亮色令牌集
private Map<String, String> tokenSetDark; // 暗色令牌集(可选)
}
8.4 Phase 3:统一暗黑模式(2-3 周)
目标:取消双配置键,改为令牌集下发。
| 动作 | 具体内容 |
|---|---|
| 后端合并双键为令牌集 | xxx.color + xxx.dark.color → tokenSet + tokenSetDark |
| 前端实现主题切换 | @media (prefers-color-scheme: dark) 注入 tokenSetDark |
| 清理旧配置键 | DUCC 中标记废弃,逐步下线 |
8.5 Phase 4:Design Tokens 流水线(4-8 周)
目标:建立设计侧 → 令牌 JSON → 多端输出的完整流水线。
| 动作 | 具体内容 |
|---|---|
| 设计团队定义令牌 | Figma Tokens 插件输出令牌 JSON |
| 引入 Style Dictionary | 令牌 JSON → CSS Variables / iOS / Android 多端输出 |
| 后端消费令牌 JSON | DUCC 配置从令牌 JSON 同步,而非手动维护 |
| 前端消费 CSS Variables | 组件全部通过 var(--token) 消费 |
九、名词解释
| 术语 | 释义 |
|---|---|
| Design Tokens | W3C 社区组标准化的设计决策载体,与平台和实现无关 |
| Global Token | 全局令牌,原始设计值(如 color-red-500: #FF0F23) |
| Semantic Token | 语义令牌,表达设计意图(如 price-main-color: {color-red-500}) |
| Component Token | 组件令牌,组件级引用(如 price-label.color: {price-main-color}) |
| Style Dictionary | Amazon 开源的设计令牌构建系统,令牌 → 多平台输出 |
| 样式下沉 | CSS 属性从前端移至后端,通过 API 下发的实践 |
| 关注点分离 | 架构原则:每个模块只负责一个关注点 |
| CSS 变量 | --var: value,前端原生主题系统机制 |
| 令牌集 | 一组语义令牌的集合,可按主题(亮色/暗色)区分 |
| BFF | Backend for Frontend,面向前端的后端聚合层 |
十、权威资料与参考文献
10.1 W3C 与行业标准
| 规范 | 说明 | 链接 |
|---|---|---|
| W3C Design Tokens Format | 设计令牌标准格式 | https://design-tokens.github.io/community-group/format/ |
| W3C CSS Custom Properties | CSS 变量规范 | https://www.w3.org/TR/css-variables-1/ |
| W3C Media Queries Level 5 | 暗黑模式媒体查询 | https://www.w3.org/TR/mediaqueries-5/ |
10.2 开源工具
| 工具 | 说明 | 链接 |
|---|---|---|
| Style Dictionary | Amazon 开源,令牌 → 多平台输出 | https://amzn.github.io/style-dictionary/ |
| Theo | Salesforce 开源,令牌管理 | https://github.com/salesforce-ux/theo |
| Figma Tokens | Figma 插件,设计 → 令牌 | https://github.com/tokens-studio/figma-plugin |
10.3 架构原则
| 原则 | 出处 |
|---|---|
| 关注点分离 | Dijkstra, 1974 |
| BFF 模式 | Sam Newman — Building Microservices |
| Feature Toggles | Martin Fowler — https://martinfowler.com/articles/feature-toggles.html |
| Design Systems API | Nathan Curtis — Design Systems API |
十一、速记口诀
🎯 核心口诀
「值可下,规不下,令牌桥,主题解」
| 口诀 | 释义 |
|---|---|
| 值可下 | 设计决策的"值"(颜色、刻度、URL)可以下发 |
| 规不下 | 设计实现的"规则"(布局、动画、排版)不应下发 |
| 令牌桥 | 用语义令牌桥接后端值和前端规则 |
| 主题解 | 暗黑模式通过前端主题系统解耦 |
🎯 判断口诀
「运营改?设备关?值或规?」
- 运营要不要改?→ 要 → 下发值
- 设备相不相关?→ 相关 → 前端自主
- 是值还是规?→ 值 → 可下发;规 → 不下发
🎯 反模式口诀
「后端不写 CSS,前端不写色值」
- 后端不写 CSS:
margin、height、textOverflow是 CSS,不是设计决策 - 前端不写色值:
#FF0F23是硬编码,应该引用var(--price-main)
本文为通用技术规范,适用于电商及类似动态化系统的前后端样式治理。基于 W3C Design Tokens 规范、关注点分离原则和业界最佳实践编写。
更多推荐


所有评论(0)