十个角色,三份几乎不重叠的清单
把三端的角色名并排放,会发现交集只有两个概念:底色和主色。其余八个,三端各说各的。
| 用途 | APP | Web | 小程序 |
|---|---|---|---|
| 主色 | primary | primary | theme |
| 主色的深/交互态 | primaryDark | primaryHover + primaryActive | btn |
| 主色的浅版 | primaryLight | 无 | tag |
| 辅色 | secondary | 无 | 无 |
| 强调色 | accent | accent + link | price |
| 文字层级 | text(只有一级) | textMain + textSub | textMain + textSub |
| 分隔 | 无 | border | divider |
| 容器 | 无 | surface | card |
| 状态色 | success / warning / danger | 无 | 无 |
| 禁用态 | 无 | 无 | btnDisabled |
三端各 10、10、10 个角色,数量一样,集合不同。这件事最直接的后果是:把 Web 的设计令牌整套搬到小程序,会得到三个用不上的键(success、warning、danger 在小程序里根本不存在),同时缺掉三个真正要用的(price、tag、divider)。反过来把小程序令牌搬到 Web,缺的是 border 和文字次级层。
所以「同一套品牌色」这句话只对主色成立。主色可以不动,其余八个角色必须按端重写。
交互态:Web 往一个方向压,APP 往两侧展开
Web 有两个交互态,APP 没有。这不是浏览器和手机的区别,是输入方式的区别:桌面上鼠标可以悬停,手指落在屏幕上没有「悬停」这个中间状态,只有按下和抬起。
库内 Web 端实测:primaryHover 比主色暗 13.9 个明度点(范围 5.3 到 14.2),primaryActive 暗 26.1 个点。后者约是前者的 1.9 倍。
APP 端不是这样。它给的是主色的两侧:primaryDark 比主色暗 14.0 个点,primaryLight 比主色亮 32.0 个点。浅版的升幅是深版降幅的 2.3 倍。
为什么浅版要更极端:深版只用来表示「按下」,而浅版要拿来当标签底色、选中区域的填充,面积大,一旦不够浅,压在上面的深色文字就没有对比度余量。深版可以只压一点点,浅版不能。
照这个量级做:hover 压 10 到 14 个明度点,active 再压一轮到两倍左右。窄于这个区间,用户点下去看不出变化;宽于这个区间,一次按压像是换了另一个品牌。
小程序的强调色叫 price,这不是命名随意
Web 和 APP 的强调色都叫 accent,是装饰性的:角标、高亮、需要被看见但不承载信息的东西。小程序没有 accent,它有一个 price。
差别的实质是:小程序的强调色服务于交易,价格是这个颜色的唯一职责。库内小程序的 price 压底色中位 4.50:1,最低 3.65:1,全部在正文门槛之上。这个数字不是巧合,价格是必须读清的信息,不是点缀,所以它按文字的标准被要求,而不是按装饰的标准。
由此可以推出一条通用判断:一个颜色是否要过对比度门槛,取决于它承载的是信息还是氛围。装饰性的强调色可以低,承载价格、状态、数量这类信息的强调色不行。同一个色相位置,两种用途,两种标准。
Web 靠分隔线,小程序靠卡片,APP 靠状态色
三端都省掉了别人的一部分角色,省掉的正好是各自不需要的。
- Web 有
surface和border,没有状态色。长页面靠细线和浅底分区,一个表单页可以有两千像素高,靠横线告诉读者「这里换了一件事」。 - 小程序 有
card、divider、tag,也没有状态色。页面是卡片流,每张卡是一个独立信息单元,分隔由卡片之间的间距完成,线条只是补充。 - APP 有
success、warning、danger,没有surface和border。移动端操作有后果,一次删除、一次提交回不去,所以它需要明确的状态反馈。
这条差异决定了移植时的第一件事:不要先搬颜色,先看这一端靠什么建立层级。Web 搬到小程序,如果只搬了颜色没搬间距,卡片会挤成一片;APP 搬到 Web,语义色没有对应场景,最后变成三个没人用的令牌。
一个反例:深色主题里 primaryLight 可能是反的
上面说 primaryLight 比主色亮 32.0 个明度点,这是库内的中位。但库内也有一套是反的:主色 #4c8df6(明度 63),primaryLight 是 #1e3a63(明度 25),比主色暗了 38 个点。
这一套是深色主题。在深色界面里,某些实现把 primaryLight 理解成「主色的浅化容器」,实际给的却是主色压暗后的深底。语义没变,值反了。
后果很具体:如果按名字直接把 primaryLight 当浅底用,在这套配色上会得到一块深蓝底配深色文字,对比度直接掉到看不见。
可执行的做法只有一条:用之前先算,别信名字。取 primaryLight 的明度与主色比一下,高于主色才可以当浅底;低于主色,它在这一套里是深容器,得配浅色文字。这一条对导入别人配色、接手别人设计系统同样成立。
如果只做一端,这三十个角色只需要四个
上面讲的是三端都要覆盖的情况。实际更多项目只做一端,或者先做一端再扩。这时候不需要把三端的角色全建出来,每一端都有一组「最小可用集」,四个角色就能撑起一页。
| 端 | 最小可用集 | 少了什么会立刻出问题 |
|---|---|---|
| Web | primary + bg + textMain + border | 没有 border,表格和表单的分区立刻失效,只能靠间距硬撑 |
| 小程序 | theme + bg + textMain + card | 没有 card,卡片流会退化成一段长文 |
| APP | primary + bg + text + primaryDark | 没有 primaryDark,按钮按下去没有任何反馈 |
四个角色能出一版能用的界面,但它有三个明显的缺口:没有次级文字(所有辅助信息只能和正文同色)、没有强调色(需要被看见的元素只能用主色)、没有禁用态(不可用的按钮和可用的长得一样)。这三个缺口不影响上线,影响的是上线之后每一次「这里怎么看着不对」。
所以顺序建议是:先四个上线,再按真实出现的问题补。补的时候按这个优先级:次级文字 → 禁用态 → 强调色。前者影响可读性,后者只影响美观,中间那个影响的是「用户会不会误以为能点」。
照做的话,改这几个值就够了
- 主色不动。同一品牌的识别靠它,三端必须一致。
- Web 加
primaryHover(暗 10 到 14 点)、primaryActive(暗到约两倍)、border、surface、textSub。不需要状态色。 - 小程序 把 Web 的
accent换成price,按文字标准要求对比度;补card、divider、tag、btnDisabled。 - APP 补
primaryDark(暗 14 点)、primaryLight(亮 32 点,且必须真的比主色亮)、secondary、三个状态色。不需要surface、border。 - 深色主题额外检查一次:
primaryLight的明度是否真的高于主色。