第1章 Material 3 主题系统概述:设计令牌架构、颜色源与角色、动态颜色机制

1.1 设计令牌架构(Design Architecture)

Material 3 的主题系统基于设计令牌(Tokens)构建。令牌是单一职责的命名变量,承载颜色、间距、圆角、阴影等原子化设计值,并通过层级结构实现从定义到组件的精确映射。

令牌分为三个层级:

  • 系统令牌(System Tokens):最低层级的原始值,例如 sys.color.primary、sys.size.small。由设计系统直接定义,不可被组件覆盖。
  • 别名令牌(Alias Tokens):对系统令牌的语义化引用,例如 color.on-primary 映射到 sys.color.on-primary,用于表达主题角色。
  • 组件令牌(Component Tokens):绑定到具体 UI 组件的局部变量,例如 md.comp.filled-button.container-color。组件令牌只能引用别名令牌,不可直接引用系统令牌。

这种分层结构保证了全局统一性与局部灵活性:修改系统令牌可批量更新所有依赖它的组件,而组件令牌又允许特定组件微调外观。

📌 工程要点
令牌应使用连字符命名,避免空格或驼峰。在代码库中优先使用 JSON/YAML 集中管理令牌文件,例如 tokens.json。Material 3 官方提供了 @material/tokens-v0-1 包作为基础令牌集。

1.2 颜色源与角色(Color Source & Roles)

Material 3 的颜色系统以种子颜色(Seed Color)为起点,通过算法生成完整的调色板(Palette),再将调色板中的色值映射到颜色角色(Color Roles)上。下表列出核心颜色角色及其语义用途:

颜色角色典型用途明暗主题对应
Primary主要品牌色,用于关键组件(按钮、顶部栏)浅-深蓝色 / 深-亮蓝色
On PrimaryPrimary 背景上的内容色(文字、图标)白色 / 黑色
Secondary次要品牌色,用于浮动按钮、选择控件浅-深紫色 / 深-亮紫色
On SecondarySecondary 背景上的内容色白色 / 黑色
Tertiary强调色,用于次要操作(链接、标签)暖色系(橙/黄)
Error错误状态色红色系
Background应用背景色浅灰 / 深灰
On Background背景上的内容色黑色 / 白色
Surface卡片、对话框的表面色浅色 / 深色
Surface Variant次要表面色(分割线、输入框填充)浅灰2 / 深灰2
Outline边框、分隔线中灰色

颜色角色在代码中通过别名令牌引用,例如 color.primary 对应 sys.color.primary。主题配置示例(JSON 格式):

{
  "seed": "#6750A4",
  "schemes": {
    "light": {
      "primary": "#6750A4",
      "on-primary": "#FFFFFF",
      "secondary": "#625B71",
      "tertiary": "#7D5260",
      "error": "#B3261E",
      "background": "#FFFBFE",
      "surface": "#FFFBFE",
      "outline": "#79747E"
    },
    "dark": {
      "primary": "#D0BCFF",
      "on-primary": "#381E72",
      "secondary": "#CCC2DC",
      "tertiary": "#EFB8C8",
      "error": "#F2B8B5",
      "background": "#1C1B1F",
      "surface": "#1C1B1F",
      "outline": "#938F99"
    }
  }
}

1.3 动态颜色机制(Dynamic Color)

动态颜色是 Material 3 的关键差异化特性:应用自动从用户当前壁纸(Wallpaper)中提取主色调,生成一整套适配明暗主题的调色板,无需开发者手动配置。

其原理涉及三个步骤:

  1. 颜色提取:从壁纸中采样像素,通过 K-Means 聚类算法识别出最显著的色相作为种子颜色。
  2. 调色板生成:基于种子颜色,使用 HCT(Hue-Chroma-Tone)色彩空间计算调色板(共 13 个色值,包含 Primary、Secondary、Tertiary、Neutral 等变体)。
  3. 角色映射:将调色板中的色值按照固定规则分配到前述颜色角色中,形成 light 和 dark 两套方案。
💡 平台差异注意
动态颜色仅原生支持 Android 12+(API 31)。在 Web / Flutter 环境中,需要手动从 @material/material-color-utilities 库中调用 HCT 工具类实现类似功能。跨平台框架(如 React Native)通常需额外桥接。

以下是一个动态颜色的完整工作流程图:

动态颜色生成流程 用户壁纸 颜色提取 (K-Means) 种子色 (Hue,Chroma) 调色板 (HCT 13色) 颜色角色分配 Light / Dark 方案 步骤依赖 HCT 色彩空间 确保可访问性对比度

1.4 常见错误与调试

  • 颜色角色缺失:动态颜色自动生成时,若种子颜色过于单一(如纯黑),可能导致某些角色(如 Tertiary)无有效值。此时应检查 Chroma 值是否低于 10—显式提供一个后备种子色。
  • 对比度不足:动态生成的 on-primary 色可能与 primary 背景对比度低于 WCAG AA 标准。Material 3 的 HCT 引擎已内置对比度调节,但需确认 `Content`(内容色)变体是否正确启用。
  • 令牌覆盖冲突:在组件级覆盖别名令牌时,不要直接写死十六进制值(如 #000),应通过 ref(sys.color.primary) 保持主题响应能力。

调试时可以使用 Material 3 官方 Theme Builder 工具(m3.material.io/theme-builder)导入种子色,观察所有角色色值及对比度验证报告。