五、前后端契约

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: 2pxtext-overflow: ellipsisheight: 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 渐进式重构四阶段

Phase 1
剥离布局属性
⭐ 低风险

Phase 2
语义化色值
⭐⭐ 中风险

Phase 3
统一暗黑模式
⭐⭐ 中风险

Phase 4
Design Tokens 流水线
⭐⭐⭐ 高风险

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.colortokenSet + 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)可以下发
规不下 设计实现的"规则"(布局、动画、排版)不应下发
令牌桥 用语义令牌桥接后端值和前端规则
主题解 暗黑模式通过前端主题系统解耦

🎯 判断口诀

「运营改?设备关?值或规?」

  1. 运营要不要改?→ 要 → 下发值
  2. 设备相不相关?→ 相关 → 前端自主
  3. 是值还是规?→ 值 → 可下发;规 → 不下发

🎯 反模式口诀

「后端不写 CSS,前端不写色值」

  • 后端不写 CSS:marginheighttextOverflow 是 CSS,不是设计决策
  • 前端不写色值:#FF0F23 是硬编码,应该引用 var(--price-main)

本文为通用技术规范,适用于电商及类似动态化系统的前后端样式治理。基于 W3C Design Tokens 规范、关注点分离原则和业界最佳实践编写。

Logo

电商企业物流数字化转型必备!快递鸟 API 接口,72 小时快速完成物流系统集成。全流程实战1V1指导,营造开放的API技术生态圈。

更多推荐