拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Material 3设计系统全解析:从设计令牌到动态配色与迁移实战

Material 3设计系统全解析:从设计令牌到动态配色与迁移实战

把 Material 3 当成"会跟着壁纸变色的换肤功能",是我见过最多的误解。去年我把 Material Design 3 官方设计指南完整过了一遍,又在一款日活几十万的产品里把主题从 M2 迁到了 M3,才意识到这套设计语言真正厉害的地方不在动态取色,而是它把"个性化表达"和"设计系统"这两个本来有点矛盾的方向拧成了一股绳。

这篇笔记是我啃完 M3 指南之后整理的精华,里面掺了不少自己落地时的实测体会。适合三类人看:准备做 Android 端改版的产品设计师、在 Figma 里搭 M3 设计系统的 UI 设计师、以及想知道"设计规范为什么这么定"的客户端开发。我不打算把官方文档翻译一遍,而是挑那些文档里没说透、但实际项目里最容易出问题的地方展开。

1. M3 的设计逻辑转向:从"统一规范"到"个性表达"

1.1 "Material You" 到底 You 在哪里

M2 时代的设计语言,核心是"统一"和"规则"。所有 App 用同一套蓝色、同一套阴影、同一套组件比例,换来的是极强的辨识度和较低的决策成本。代价也很明显:产品之间越来越像,个性化只能靠插画和运营位硬撑。

M3 把关键词换成了 expressive、personalization、adaptive。Material You 这个命名里的"You",指的不是设计师,而是终端用户——系统不再由设计规范单方面规定"你必须是这个颜色",而是根据用户的使用场景、壁纸、甚至是用户主动选择的内容,动态生成一套视觉主题。注意,这里的关键不是"换肤",而是"生成规则":颜色不是设计师手挑的几个色值,而是通过算法从一个种子色(seed color)推导出来的完整体系。

这个转向对设计工作流的影响是结构性的。以前我们交设计稿,交付的是"一组颜色";现在交的是"一条颜色生成链路"加"链路出问题时的兜底策略"。我刚做迁移时老用旧思维改色板,改到一半就发现,只要 seed color 一变,整套色板都会跟着变,手动微调的意义很小。你得先接受这个新逻辑,再谈具体落地。

1.2 设计令牌:M3 的底层地基

M3 整个系统都是建立在设计令牌(design tokens)之上的。颜色有颜色令牌,形状有形状令牌,字体有字体令牌,连高度和状态层都有自己的 token 体系。

为什么要强调这一点?因为 M3 的组件规范和实现代码之间,靠的正是这层抽象。你在 Figma 里看到的 M3 组件库,本质上是一个"长着 M3 外观的令牌驱动组件"——改一个主色 token,全局按钮、卡片、导航栏全部跟着变。到了代码侧,Jetpack Compose 的 Material3 库和传统 XML 的 Material Components for Android 1.5+ 也都实现了同一套 token 体系。

实际操作中这意味着:改主题不再等于改组件。M2 时代换主题色,经常要逐个检查按钮、输入框、选中态有没有漏改;M3 时代你只要维护好 token 的映射关系,组件会自动收敛。我后来在 Figma 里搭组件库时,把所有颜色引用全部指向 style 变量,而不是直接填色值,改版的效率提升非常明显。如果你现在还是"一个组件一个组件地上色",建议先停一下,把 M3 这套令牌思维建立起来再动手。

2. 动态配色推演链:一个种子色如何长成整套主题

2.1 种子色 → 14 级音调色板 → 色彩角色

M3 动态配色的完整链路是这样的:你先给一个 seed color(种子色),Material Color Utilities 库会把种子色映射到 HCT 色彩空间,然后生成 14 个明度档位的音调色板(tonal palette)。这 14 个档位从 0 到 100,分别是 0、10、20、25、30、40、50、60、70、80、90、95、99、100。注意不是均匀分布的,中间密两端疏,因为人眼对中间区域的明度变化更敏感。

整个系统会生成五个基准色板:

  • primary:品牌主色,对应系统里的主操作、选中态
  • secondary:次要强调色,用于辅助操作和低权重强调
  • tertiary:第三强调色,纯粹为了"表达力"而存在,用来制造视觉层次
  • neutral:中性色,承载 surface、background 等大面积底色
  • neutral-variant:中性变体,用于次要文字、描边、分割线

每个色板都能在 14 个音调上取值,这就引出下一步——色彩角色。

2.2 色彩角色是语义,不是色值

M3 最容易被新手忽略的地方,是它把颜色从"具体色值"升级成了"语义角色"。也就是说,你在设计稿里不应该说"这个按钮是 #6750A4",而应该说"这个按钮的容器是 primary"。

常见的色彩角色有这么几组:

角色典型用途说明
primary / on-primary主按钮、选中态on-* 表示"放在该颜色上面的内容颜色"
primary-container / on-primary-container次级强调容器比 primary 浅一档,常用于选中卡片
secondary / tertiary辅助强调M3 特色,给界面更多层次
surface / on-surface页面底色与主文字surface 是中性色,不带彩度
surface-variant / on-surface-variant次要底色与次要文字比 surface 略深,适合分组背景
outline / outline-variant描边、分隔线替代 M2 时代部分 divider 用法
surface-container-low 到 highest卡片层级底色用"容器明度"表达层级,替代阴影

这套语义体系的妙处在于:当 seed color 变化时,primary 的色值变了,但"主按钮该用什么颜色"这个语义没变,所以组件不会错乱。反过来说,如果你在项目里用"浅紫色""深灰色"这种描述来维护颜色,那 M3 的迁移前期就得花大力气把词表统一掉。

我踩过的一个具体坑是:直接把旧品牌色往里塞进 primary 角色,结果对比度一塌糊涂。后来才明白,primary 角色只负责"语义",品牌色的表达应该交给 primary-container 或动态取色之后的色板,而不是咬着原始色值不放。

2.3 实操笔记:品牌色被稀释的补救办法

品牌团队通常会很在意"品牌蓝必须是 #1565C0"这件事。M3 的动态取色天然会稀释品牌色,因为算法是从种子色推导整个色板,出来的 primary 不可能是原来的精确色值。

我的处理办法分三步:

  1. 把品牌色作为 seed color 输入生成 baseline scheme(基线方案),不要手动造色板。
  2. 对必须保持品牌辨识度的场景(比如 Logo、品牌页背景),使用静态色资源,不走动态角色。
  3. 如果主 CTA 在动态取色后对比度不足(黄色、橙色种子色特别容易翻车),把 CTA 固定到静态 primary,或者转移到"容器角色 + 足够深的 on-primary-container"组合上。

另外一定要在各种壁纸下做多主题测试。动态取色在浅色壁纸上可能给出很浅的色板,深色壁纸又可能给出过饱和的色板,这两个极端场景都得有兜底规则。Figma 的官方 Material Theme Builder 插件可以直接预览种子色变化带来的全组件效果,建议选种子色时就在里面调,别在 Sketch 里脑补最后效果。

补充一个 HCT 色彩空间的知识点:M3 之所以不用我们熟悉的 HSL/HSB,是因为 HSB 在感官上不均匀——同样改 10 个单位的饱和度,蓝色看起来变化不大,橙色却已经刺眼了。HCT 把色调(Hue)、色度(Chroma)、明度(Tone)分开建模,其中 Tone 方向的变化在感知上是均匀的。这就是为什么 M3 的调色板不会出现"某个颜色突然变艳或者变脏"的情况,整套配色体系能保持统一质感。

3. 形状、状态层与音调高度:M3 组件"变软"的三层机制

很多人第一眼看到 M3 的感觉是"变圆了、变软了"。这个观感不是随便来的,而是三层底层机制同时作用的结果:新的形状令牌体系、状态层的引入、以及用音调高度替代纯阴影。

3.1 一套形状令牌,覆盖所有组件

M3 定义了一套从"无圆角"到"全圆角"的形状尺度,共 7 档:none(0dp)、extra small(4dp)、small(8dp)、medium(12dp)、large(16dp)、extra large(28dp)、full(完全圆角)。

所有组件的圆角都从这 7 档里取值。比如:

  • 按钮类组件通常用 full,所以 M3 的填充按钮看起来是"胶囊形"
  • 卡片多用 medium 到 large
  • 输入框、列表项偏 small 到 medium
  • 对话框用 extra large 到 full

M2 时代圆角的取值比较随意——同一个卡片在不同 App 里有 4dp、8dp、12dp 各种尺寸,团队内部对齐成本很高。M3 把圆角抽象成"形状令牌"之后,主题切换时只需要改一套形状变量,所有组件同步变化。这跟颜色令牌的逻辑完全一样:语义化、变量化、一处修改全局生效。

3.2 状态层:按压、悬停、拖拽的透明度标准

M3 的交互状态反馈是一个很容易被忽略但极其重要的机制:状态层。它不再是"按下去换个背景色",而是在组件之上叠加一层半透明遮罩,通过透明度表达当前状态。

官方给出的标准透明度如下:

状态状态层透明度
Hover(悬停)8%
Focus(聚焦)10%
Pressed(按压)10%
Dragged(拖拽)16%

为什么这样做更合理?因为 M2 的做法是直接改组件底色,浅色主题和深色主题需要分别设计两套按压色,很容易漏。M3 的状态层是叠加在任意 surface 颜色之上的,只要底色支持叠加,深浅主题下反馈的"感知强度"会保持一致。这在设计系统层面是一个巨大的维护成本节约。

实际落地时注意:状态层用的是颜色的 alpha 通道,别在 Figma 里直接改填充色。M3 官方组件库里这些都已经封装好,但如果团队是自建组件库,一定要把状态层做成组件属性(hover、focused、pressed、dragged),让使用方直接调用,而不是手工拉透明度。不然做出来的效果在暗色模式下会明显偏深或者偏脏。

3.3 音调高度:用"色调"取代"阴影"

M3 最让我觉得"反直觉"的改动,是用音调高度(tonal elevation)来部分替代阴影。

传统设计中,层级靠阴影表达:卡片阴影越重,离页面越"高"。但深色模式下阴影几乎不可见,层级就垮了。M3 的解法是做了一层"面染色"(surface tint):surface 颜色本身会叠加一层来自 primary 的轻微色调,层级越高,叠加越明显。

具体来说,M3 定义了 0 到 5 共六个高度层级,每升一级,surface 上叠加的色调就越深一点点。于是卡片的层级不再只靠投影,而是靠"底色深浅 + 描边 + 轻阴影"的组合来表达。

我在实际设计里体会最深的是:M3 的卡片在一张白色页面上,看起来是"一张稍微带点灰紫色的浅色浮层",而不是"一个以阴影撑起来的白色方块"。这个差异在深色模式下尤其明显——深色主题里卡片和背景的区分度,几乎完全靠 tonal elevation 维持,阴影反而成了配角。

做深色模式时,别把 M2 那套"深灰底 + 更深灰卡片 + 阴影"直接搬过来。先检查你的 surface 容器层级色有没有拉开,这个优先级高于调阴影。

4. 字阶表重构:15 个样式背后的排版逻辑

4.1 五类三级:Display、Headline、Title、Body、Label

M3 的字体系统从 M2 的 12 个样式重构为 15 个样式,分为五类,每类大中小三档:

类别大中小
Display57 / 6445 / 5236 / 44
Headline32 / 4028 / 3624 / 32
Title22 / 2816 / 24(字重 500)14 / 20(字重 500)
Body16 / 2414 / 2012 / 16
Label14 / 20(字重 500)12 / 16(字重 500)11 / 16(字重 500)

表格里第一个数字是字号(sp),第二个是行高(sp)。默认字体是 Roboto,但 M3 对字体本身没有强绑定,核心是这套节奏关系。

4.2 和 M2 的对比:哪些字号变了、为什么

M2 里有个 overline 样式(全大写小字号,常用于列表分类标签),M3 把它并进了 Label 体系。M2 的 subtitle 两档并进了 Title 体系。最大的变化是 Display 序列——M3 最大字号拉到了 57sp,明显是为了适配大屏设备、折叠屏和沉浸式浏览场景。以前我们做手表、手机、平板共用一套字阶会有点紧张,M3 给大屏留足了空间。

另外注意字重变化:M2 的标题大量使用 Medium(500),M3 的 Display 和 Headline 类都回归了 Regular(400),只有 Title 和 Label 类保留 Medium。这个变化很微妙,但影响很大——M3 的标题看起来"更轻盈、更透气",正文和标题的界限不再只靠字号,而是靠字重和行高共同建立的节奏。

4.3 落地建议与常见误用

我在项目里总结出三条实用规则:

  1. 正文优先选 Body Large 或 Body Medium。Body Small(12sp)虽然省空间,但作为正文阅读体验已经很吃力了,只适合辅助信息。
  2. Display 别乱用。很多人看到 57sp 觉得震撼,恨不得首屏放个 Display Large,结果页面信息密度暴跌。Display 类适合启动页、空状态、品牌表达页这几个少数场景。
  3. 适配系统字体缩放。Android 上 sp 单位会跟随系统字号设置变化,超大字号模式下布局很容易被撑爆。M3 指南要求文本在系统放大 200% 时依然完整可用,建议用 maxLines + ellipsis 控制,并给关键文本区域留足换行空间。

还有一个细节:M3 的图层面板里,Label 类样式一般都带字距(tracking),比如 Label Large 的字距是 0.1,Body Large 是 0.5。Figma 里如果从 M2 项目复制文本过来,字距设置可能残留旧值,肉眼看起来"不太对"但又说不出哪里不对。迁移时建议把字阶的 tracking 统一重置成 M3 标准值。

5. 组件变化速查与迁移实操记录

5.1 重点组件变化清单

M3 对组件库做了不少"改名字、调形态、加变体"的操作,这里列一份快速对照:

M2 组件M3 对应主要变化
Contained ButtonFilled Button圆角变大,高度 40dp,胶囊化
无Filled Tonal Button新增,用 secondary-container 背景,适合次级 CTA
Text Button / Outlined Button保留形态微调,状态层机制统一
FAB / Mini FABFAB / Small FAB / Large FAB / Extended FAB新增 Small(40dp)和 Large(96dp)
Bottom NavigationNavigation Bar高度统一 80dp,采用新的指示条样式
无Navigation Rail新增,适合平板和横屏
Top App BarSmall / Medium / Large 三档支持折叠和标题缩放动效
Switch保留但重绘轨道加图标,圆润度大幅提升
Snackbar保留但重绘圆角变化明显,默认用 full 圆角
无Badge新增小组件,替代"红点 + 数字"的零散方案

这份清单不用背,重点是理解变化的逻辑:M3 整体朝着"更圆、更轻、更多层级"的方向走。按钮从 36dp 加到 40dp、导航从扁平 tab 变为带指示条的 bar,都是为了在触屏和视觉识别之间取得更好的平衡。

5.2 迁移到 M3 的推荐顺序

从 M2 迁 M3,别指望一天搞定。我推荐的顺序是:

  1. 先用 Material Theme Builder 生成 baseline 主题 token,把颜色、形状、字阶三套 token 建好。
  2. 把页面底色的语义从"白色/深灰色"改成 surface 系列角色,这是后续所有改动的基础。
  3. 替换高感知组件:按钮、导航栏、输入框、卡片。
  4. 统一状态层,把 hover / focus / pressed / dragged 都接到组件属性上。
  5. 最后处理大标题、动效、Badge 这类锦上添花的东西。

代码侧的话,Jetpack Compose 直接用 material3 库,改一个 colorScheme 就能切换主题;XML 项目需要升到 Material Components for Android 1.5.0 以上,然后应用 Theme.Material3.* 主题。建议先在分支上做一个最小 Demo,把主题切过去看看所有页面的"脸色",再决定是全局改还是渐进式改。

5.3 迁移实测踩过的三个坑

第一个坑是误把"动态取色"当成全站换肤。团队一开始以为所有元素都会智能适配用户壁纸,结果测试时发现某个页面里图表配色和壁纸撞色了。核心原因是图表颜色不应该用动态角色,需要独立维护一套语义色。记住:动态取色只影响色彩角色,不改变你为特定业务场景预留的静态色策略。

第二个坑是Figma 组件库的旧 token 残留。从 M2 切换到 M3 组件库时,旧页面上有些样式还指向老的命名(比如 "Primary" 之类),开发照着还原,颜色就出现了偏差。后来我把所有样式清理了一遍,强制所有组件引用新的 token 名,并且跑了一次截图回归对比才解决。迁移不只是换组件,还要清命名空间。

第三个坑是对比度问题。M3 的 primary-container 在某些种子色下,和 on-primary-container 的对比度达不到 WCAG AA 标准,尤其是橙色、黄色系种子色。这会导致浅色文字在浅色容器上很难看清。这属于设计系统层面的硬伤,不能靠"调一下透明度"蒙混过关,要么换种子色,要么对该角色使用固定色值。

最后说点个人的体会。M3 这套设计的门槛其实比 M2 高——它要求设计师理解"语义化""令牌化""算法生成"这些偏工程的思维,而不是单纯地挑颜色、摆组件。但也正因为门槛高,做出来的东西一旦跑通,产品之间的辨识度和质感差异会非常明显。如果你正准备迁移,别急着追求 100% 还原官方 spec,先把色彩角色、状态层、音调高度这三个底层语义理清楚,组件长什么样反而是最后一步的事。

返回列表