电商系统前后端样式治理规范(一)
·
电商系统前后端样式治理规范
定位:适用于电商及类似动态化系统的前后端样式分层设计规范,提供决策框架、接口契约、实施路径与自检清单。
一、问题陈述
1.1 现象
架构的电商系统中,后端 Java 代码逐渐承载了越来越多的 CSS 属性——颜色值、字号、间距、圆角、甚至 margin、padding、lineHeight 被搬进 VO 对象,通过 API 下发给前端。典型表现:
- 后端 VO 字段与 CSS 属性 1:1 镜像(如
ComponentStyle含 19 个 CSS 属性字段) - 配置中心堆砌数十个样式配置键,暗黑模式使配置键翻倍
- 后端 Java 代码中出现硬编码像素值(如
height: "6",marginLeft: "0 0 0 2") - 色值散落在多个 Java 类的默认值中,缺乏统一设计系统约束
1.2 根因
| 根因 | 表现 | 本质 |
|---|---|---|
| 运营需要动态调色 | 色值通过配置中心下发 | 合理需求,但实现方式越界 |
| A/B 实验需要切换样式 | 整个 CSS 对象下发 | 混淆了"值"与"规则" |
| 大促需要一键换肤 | 配置键随场景线性增长 | 缺乏令牌抽象 |
| 暗黑模式适配 | 配置键翻倍 | 前端主题系统缺位 |
| 前后端职责模糊 | 布局属性也由后端下发 | 缺乏分层规范 |
1.3 核心矛盾
后端需要控制"设计决策"的值,前端需要控制"设计实现"的规则。两者混在一起,导致后端成了 CSS 容器,前端成了无脑渲染器。
二、设计原则
原则一:值可下,规不下
| 概念 | 定义 | 归属 | 示例 |
|---|---|---|---|
| 值 | 设计决策的原子数据 | 后端下发 | #FF0F23、4px、sm |
| 规 | 设计实现的 CSS 规则 | 前端自主 | margin-left: 8px、display: flex |
判断方法:把属性值替换为另一个合法值,如果 UI 语义不变则属于"规",如果 UI 语义改变则属于"值"。
color: #FF0F23→ 改为#000000,UI 语义从"价格红"变为"黑色" → 值margin-left: 8px→ 改为12px,UI 语义不变(只是间距微调) → 规border-radius: 4px→ 改为8px,UI 语义从"小圆角"变为"大圆角" → 值(刻度变化)
原则二:令牌桥接,而非属性透传
后端不下发 CSS 属性名,而是下发语义令牌。前端通过 CSS 变量层消费令牌。
❌ 当前:后端 → { color: "#FF0F23", fontSize: "14", margin: "0 0 0 2" } → 前端直接绑定 style
✅ 规范:后端 → { tokens: { "price-main": "#FF0F23", "price-size": "sm" } } → 前端 CSS 变量映射 → 组件消费
原则三:主题前端解,不靠配置翻倍
暗黑模式、品牌换肤等主题切换,由前端 CSS 变量 + 媒体查询 / 属性选择器实现,后端只下发令牌值集。
原则四:设计系统单一信源
所有色值、间距刻度、字号刻度的原始定义,来源于设计系统(Design System),后端配置中心是信源的下游消费者,不是信源本身。
三、决策框架
3.1 属性归属决策树
3.2 三区分类表
🔴 红区 — 后端下发(设计决策的值)
| 类别 | 典型属性 | 下发形式 | 变更驱动 |
|---|---|---|---|
| 品牌色/业务色 | 主价格色、促销色、标签色 | 语义令牌 price-main: #FF0F23 |
运营/A/B实验 |
| 语义色 | 成功色、警告色、错误色 | 语义令牌 color-success: #07C160 |
设计规范 |
| 间距刻度 | 卡片圆角、按钮圆角 | 刻度令牌 radius-md: 8px |
设计规范 |
| 字号刻度 | 标题字号、正文字号 | 刻度令牌 font-size-title: 18px |
设计规范 |
| 图标资源 | 图标 URL、插画 URL | URL 字符串 | 运营/设计 |
| 文案 | 按钮文案、提示文案 | 字符串 | 运营/法务 |
🟢 绿区 — 前端自主(设计实现的规则)
| 类别 | 典型属性 | 理由 |
|---|---|---|
| 布局策略 | display, flex-direction, grid-template, position |
设备/屏幕相关 |
| 组件间距 | margin, padding, gap |
组件内部自治 |
| 尺寸计算 | width, height, min-height, max-width |
设备 DPI 相关 |
| 排版规则 | line-height, text-overflow, white-space, word-break |
排版是前端专长 |
| 动画效果 | transition, animation, transform |
性能相关 |
| 响应式 | @media, container-query |
屏幕相关 |
| 安全区域 | safe-area-inset-* |
设备相关 |
| 字体栈 | font-family |
平台相关(iOS/Android 字体不同) |
| 交互状态 | :hover, :active, :focus, :disabled |
交互相关 |
| 加载态 | 骨架屏、占位符、shimmer | 加载体验 |
🟡 黄区 — 协商决定(需根据业务场景判断)
| 类别 | 服务端角色 | 前端角色 | 协商策略 |
|---|---|---|---|
| 楼层容器间距 | 下发间距刻度(如 spacing-md) |
自主决定具体像素值 | 刻度下发,像素前端算 |
| 容器圆角 | 下发圆角刻度(如 radius-lg) |
自主决定组件内部圆角 | 大圆角下发,小圆角前端 |
| 字号 | 下发字号刻度(如 font-size-sm) |
根据 DPI 缩放 | 基准下发,缩放前端 |
| 弹层高度 | 下发高度比例(如 75%) |
自主决定最小/最大高度 | 比例下发,边界前端 |
| 截断行数 | 一般不下发 | 自主决定 | 除非运营需要动态控制 |
四、令牌架构规范
4.1 三层令牌模型
层级职责:
| 层级 | 定义者 | 消费者 | 变更频率 | 示例 |
|---|---|---|---|---|
| Global | 设计系统 | 语义层 | 低(设计规范升级时) | color-red-500: #FF0F23 |
| Semantic | 设计系统 + 运营 | 组件层 | 中(运营调整时) | price-main-color: {color-red-500} |
| Component | 前端组件 | CSS 变量 | 低(组件重构时) | price-label.color: {price-main-color} |
关键约束:
- 组件不直接引用 Global 令牌,必须通过 Semantic 令牌间接引用
- 后端只下发 Semantic 令牌,不下发 Global 令牌,不下发 Component 令牌
- 前端 CSS 变量层对应 Component 令牌
4.2 令牌类型定义
| 令牌类型 | $type 值 |
值格式 | 示例 |
|---|---|---|---|
| 颜色 | color |
HEX / RGB / RGBA | #FF0F23 |
| 尺寸 | dimension |
数值 + 单位 | 8px, 0.5rem |
| 字号 | fontSize |
数值 + 单位 | 14px |
| 字重 | fontWeight |
数值 / 关键字 | 400, bold |
| 圆角 | borderRadius |
数值 + 单位 | 4px |
| 间距 | spacing |
数值 + 单位 | 8px |
| 阴影 | shadow |
CSS shadow 值 | 0 2px 4px rgba(0,0,0,0.1) |
| 时间 | duration |
数值 + 单位 | 200ms |
4.3 后端下发接口规范
请求
GET /api/wareId=xxx&theme=auto
响应(令牌区)
{
"tokenSet": {
"price-main-color": "#FF0F23",
"price-second-color": "#888B94",
"price-main-bg-color": "#FF0F23",
"price-main-text-color": "#FFFFFF",
"tag-danger-bg": "#FF0F23",
"tag-danger-text": "#FFFFFF",
"tag-success-bg": "#07C160",
"tag-success-text": "#FFFFFF",
"card-radius": "8px",
"button-radius": "4px",
"spacing-card": "12px"
},
"tokenSetDark": {
"price-main-color": "#FF6B6B",
"price-second-color": "#AAAAAA",
"price-main-bg-color": "#FF6B6B",
"card-radius": "8px"
}
}
规范约束:
tokenSet:亮色令牌集(必填)tokenSetDark:暗色令牌集(可选,仅包含与亮色不同的令牌)- 令牌名使用
kebab-case命名 - 令牌值只包含值,不包含 CSS 属性名
- 不下发布局属性(margin/padding/height/width/lineHeight 等)
前端消费
/* Step 1: 注入令牌到 CSS 变量 */
:root {
/* 由 JS 将 tokenSet 注入 */
--price-main-color: #FF0F23;
--price-second-color: #888B94;
--card-radius: 8px;
}
/* Step 2: 暗黑模式覆盖(由 JS 将 tokenSetDark 注入) */
@media (prefers-color-scheme: dark) {
:root {
--price-main-color: #FF6B6B;
--price-second-color: #AAAAAA;
}
}
/* Step 3: 组件消费令牌 */
.price-label {
color: var(--price-main-color);
font-size: 14px; /* 前端自主,不从后端下发 */
line-height: 1.4; /* 前端自主 */
margin-left: 8px; /* 前端自主 */
}
.card {
border-radius: var(--card-radius);
padding: 12px; /* 前端自主 */
background: var(--card-bg, #FFFFFF); /* 令牌 + 兜底值 */
}
更多推荐


所有评论(0)