第3章 数据结构:颜色、字型、间距、形状等命名规范与嵌套规则,可扩展性设计
3.1 原理概述
Material 3 主题云分发的核心是 Design 的标准化数据结构。 是主题属性的最小原子单元,每一枚 包含一个语义化名称和一个关联值。在云端分发场景下, 结构必须同时满足三项目标:
- 命名自解释:开发者读取 名即可理解其用途,无需翻阅文档。
- 嵌套可推导:通过层级关系自动推导变体(如浅色/深色、交互状态、密度缩放)。
- 扩展易附加:在不破坏现有结构的前提下,允许品牌自定义新 或覆盖默认值。
本章将逐一拆解颜色、字型、间距、形状的命名规范、嵌套规则及可扩展性设计模式。
3.2 命名规范
命名采用 语义化 + 层级分隔符(点号或中线) 范式。推荐使用小写字母,单词间以中线连接,层级间以点号分隔。状态后缀统一附加在末端。
| 类别 | 命名模式 | 示例 |
|---|---|---|
| 颜色 | color.类别.角色.变体.状态 | color.primary.default.base、color.primary.default.hovered |
| 字型 | type.尺寸族.层级.变体 | type.display.large.regular、type.body.medium.bold |
| 间距 | spacing.方向.等级 | spacing.inline.sm、spacing.inset.lg |
| 形状 | shape.元素.圆角等级 | shape.card.md、shape.button.full |
3.2.1 颜色命名细则
颜色 结构固定为四段:color.{角色}.{变体}.{状态}。角色包括 primary、secondary、tertiary、error、surface、outline 等。变体为 default、container、on- 前缀派生。状态可选 base、hovered、focused、pressed、disabled。
✅ 正确命名示例:
color.primary.default.base → #6750A4
color.primary.default.hovered → #7C67B1
color.primary.container.base → #EADDFF
color.surface.default.base → #FFFBFE
color.error.default.hovered → #B3261E
3.2.2 字型命名细则
字型 结构:type.{尺寸族}.{层级}.{字重}。尺寸族指 display、headline、title、body、label。层级用 large、medium、small 描述相对大小。字重值域固定为 regular、medium、bold。
type.display.large.regular → font-size: 57px, line-height: 64px, weight: 400
type.headline.medium.bold → font-size: 28px, line-height: 36px, weight: 700
type.body.small.regular → font-size: 12px, line-height: 16px, weight: 400
3.3 嵌套规则
嵌套规则决定了 如何从通用到特殊层层覆盖。整个主题本身为一个嵌套对象,根节点下按类别分支,每个分支内再按角色、变体、状态逐级嵌套。
- 类别嵌套:
color、type、spacing、shape属于顶层键。 - 状态嵌套:状态(hovered/focused/pressed)必须位于变体之下,而非同级。
- 变体嵌套:如
primary下同时存在default和container,它们共享primary的语义命名空间。
解析时采用 最长匹配优先 规则:读取完整 名后,按点号逐层查找,若当前层级缺失则向上回溯至最近父节点取得默认值(适用于未显式声明状态的场景)。
3.3.1 嵌套覆盖示例
// 基础主题
{
"color": {
"primary": {
"default": {
"base": "#6750A4",
"hovered": "#7C67B1"
},
"container": {
"base": "#EADDFF"
}
}
}
}
// 用户覆盖只指定 hovered 状态
{
"color": {
"primary": {
"default": {
"hovered": "#5A3E8A" // 仅覆盖 hovered,base 仍从上方继承
}
}
}
}
3.4 可扩展性设计
要实现云端与本地主题的无缝扩展,必须引入 别名引用 和 overlay 机制:
- 别名引用:允许 直接引用另一 的路径。解析器将所有别名展开为最终值,避免重复赋值。
- 覆盖层(Overlay):云分发时每次合并仅 diff,用户只需提供被修改的 ,未修改部分自动从基线继承。
- 自定义 命名空间:品牌方可在任意类别下添加
custom.前缀的 ,系统保证不与官方 冲突。
💡 扩展实践: 第三方主题包应遵循
vendor.品牌名.类别.角色... 模式。例如 vendor.acme.color.brand.light,云服务端通过白名单允许访问。
3.5 数据结构完整实现示例(JSON)
以下是一个符合 Material 3 规范的 主题片段,展示了颜色、字型、间距、形状的完整嵌套结构:
{
"$schema": "https://m3.themes.io/-v1.json",
"version": "1.0.0",
"extensible": true,
"color": {
"primary": {
"default": { "base": "#6750A4", "hovered": "#7C67B1", "focused": "#A28FC9", "pressed": "#4F378B" },
"container": { "base": "#EADDFF", "hovered": "#DEC8FA", "focused": "#C9B0E6", "pressed": "#A88AD4" }
},
"secondary": {
"default": { "base": "#625B71", "hovered": "#757283", "focused": "#9A97A8", "pressed": "#4A4458" },
"container": { "base": "#E8DEF8", "hovered": "#D7C8F0", "focused": "#C4B0E0", "pressed": "#A792C8" }
},
"surface": {
"default": { "base": "#FFFBFE", "hovered": "#F5F0F6" },
"container": { "base": "#F7F2FA" }
}
},
"type": {
"display": { "large": { "regular": "3.5625rem/4rem 400", "bold": "3.5625rem/4rem 700" } },
"headline": { "large": { "regular": "2rem/2.5rem 400" } },
"body": { "large": { "regular": "1rem/1.5rem 400" } },
"label": { "large": { "regular": "0.875rem/1.25rem 500" } }
},
"spacing": {
"inline": { "xs": "4px", "sm": "8px", "md": "16px", "lg": "24px" },
"inset": { "xs": "4px", "sm": "8px", "md": "16px" },
"stack": { "xs": "2px", "sm": "4px", "md": "8px", "lg": "12px" }
},
"shape": {
"card": { "sm": "4px", "md": "8px", "lg": "16px" },
"button": { "full": "50%" },
"input": { "default": "4px" }
},
"_aliases": {
"color.primary.default.hovered": "color.primary.default.hovered",
"type.body": "type.body.large.regular"
}
}
3.6 调试与验证
数据结构在云分发前必须通过以下检查:
| 检查项 | 工具/方法 | 典型命令 |
|---|---|---|
| 命名合规 | Lint CLI | m3--lint --schema v1 theme.json |
| 别名完整性 | 引用解析器 | m3-resolve-aliases theme.json --output resolved.json |
| 嵌套深度 | JSON 深度检测 | jq '[paths(scalars)] | length' theme.json |
| 覆盖层冲突 | Diff 合并器 | m3-overlay-merge base.json overlay.json --detect-conflict |
3.7 常见错误
⚠️ 高频错误清单:
- 命名歧义:使用
color.primary.dark而非color.primary.dark.hovered,导致状态层级丢失。 - 覆盖丢失:仅提供部分变体时忘记声明
base,解析器向上回溯后取到错误的全局默认值。 - 嵌套循环:别名 A 引用 B,B 又引用 A,造成无限递归(应使用 DAG 检测器预校验)。
- 层级过深:超过 5 层嵌套(如
color.primary.default.container.hovered.pressed)会显著降低解析速度,建议控制在 4 层以内。
3.8 性能与稳定性优化
- 扁平化缓存:生产环境将嵌套 展开为扁平 key-value 结构(如
color.primary.default.base→#6750A4),省去运行时递归解析。 - 引用预编译:构建阶段将所有别名替换为实际值,用户侧仅存储最终值,避免拉取时进行多次网络请求。
- 按需加载:云端按类别分片存储 (如
color.json、type.json),客户端按需拉取,减少初始载荷。 - 版本哈希:在 包顶层添加
content_hash字段,CDN 根据 hash 做增量更新,降低全量刷新频率。