外观
组件状态与命名
你在 22 组件里把微信的按钮、气泡做成了可复用组件。但组件不是"一个长相"——它会变:被碰、被按、被禁用、在加载。状态是组件的性格,命名是组件的身份证。给每个状态定齐、起规范名,设计系统才"可维护"。
状态混乱、名字乱叫,从哪来
真产品里的混乱是这样发生的:同一个组件,有的页面按下变深绿、有的按下变灰;有的禁用是灰、有的禁用是"还能点但没反应";同学问"那个浅绿按钮是啥状态",你答不上来——因为它没名字。根子在于:没人规定"组件该有哪些状态",也没人规定"状态该怎么命名"。于是每个页面各做各的,设计系统就乱了。专业解法是两件事:先给组件定一套状态集,再给每个状态起统一的规范名。状态和命名,就是设计系统的秩序。
六种状态:组件的"性格清单"
一个标准组件,通常有 6 种状态:默认(平时躺着不动)、悬停(鼠标停在上面)、按下(按下去的瞬间)、禁用(现在不能点)、加载(正在做事等结果)、聚焦(键盘/屏幕阅读器定位到它)。
| 状态 | 英文 | 什么时候出现 | 微信例子 |
|---|---|---|---|
| 默认 | normal | 平时躺着不动 | 没碰过的绿色按钮 |
| 悬停 | hover | 鼠标停在上面(桌面端) | 电脑微信里鼠标悬停的消息 |
| 按下 | pressed | 手指/鼠标按下去的瞬间 | 按住"发送"那一刻变深 |
| 禁用 | disabled | 现在不能点 | 发送框为空时的"发送"键(灰的) |
| 加载 | loading | 正在做事、等结果 | 刷新朋友圈时按钮转菊花 |
| 聚焦 | focus | 键盘/屏幕阅读器定位到它 | 搜索框被 Tab 选中时的描边 |
关键澄清:"状态 = 颜色变化"是误区。状态远不止变色——还有禁用(能不能点)、聚焦(键盘可访问)、加载(在等什么)。颜色只是状态最显眼的表现之一,不是全部。每种状态都应承载一种明确的含义,绝不乱闪。
标准组件 6 态:normal / hover / pressed / disabled / loading / focus。
命名规范:命名即规范
状态要能被团队统一调用,就得命名。命名不是"随便起个顺口的",而是规范本身——名字长什么样,决定团队怎么沟通、怎么改。约定是组件 + 变体 + 状态,用连字符串起来。
| 组件 | 变体 | 状态 | 规范全名 |
|---|---|---|---|
| btn | primary | pressed | btn-primary-pressed |
| btn | primary | normal | btn-primary(默认态可省略) |
| msg | bubble | normal | msg-bubble |
| cell | list | pressed | cell-list-pressed |
关键澄清:"命名随便,反正是内部的"是误区。命名即规范——一个 btn-primary-pressed 的名字,既告诉你是哪个组件、哪个样式、哪个状态,又让全团队、全工具、全代码都喊同一个名字。名字一旦乱,设计就散。
命名规范 = 组件 + 变体 + 状态,连字符串;默认态 normal 可省略。
状态名引用语义 Token
上一节(23)的语义 Token 回答"这个状态该用什么颜色"——状态不是凭空变色的,它引用语义 Token。看不看语法都行,先看关系:color-bg 是品牌绿,需要白色界面底色时用 color-surface,两者分得清;组件按下态的名字 btn-primary-pressed,用 color-bg-pressed 这个 Token 的颜色。
css
--color-bg: var(--color-brand-primary); /* 背景绿 = 品牌绿 */
--color-surface: #FFFFFF; /* 界面底色 = 白 */
--color-bg-pressed: #049A49; /* 按下 = 更深绿 */
.btn-primary-pressed { background: var(--color-bg-pressed); } /* 按下背景 */一句话:状态名是"指纹",Token 是"颜色档案"。把组件的 pressed 态说成"btn-primary-pressed 用 color-bg-pressed 的颜色"——改名、改色各管各的。这就是设计系统"可维护"的真正含义:名字定身份,Token 定颜色,各司其职。
状态名管"身份"(谁的哪个状态),Token 管"颜色数据源"(该是什么颜色)。
微信"发送"键:从状态到命名到 Token 捋一遍
把微信的绿色"发送"键完整走一遍:常态是绿底白字,按下变深绿,发送框为空时灰底点不动、加载时绿底转菊花。把它从状态到命名到 Token 列成一张表,就是"发送"键的完整状态规范——你说"btn-primary-pressed 调深一点",开发改的是它引用的 color-bg-pressed 这个 Token。
| 状态 | 规范名 | 引用 Token | 长相 |
|---|---|---|---|
| 默认 | btn-primary | color-bg(品牌绿)+ color-text-inverse(白字) | 绿底白字 |
| 按下 | btn-primary-pressed | color-bg-pressed | 变深一点的绿 |
| 禁用 | btn-primary-disabled | color-bg-disabled + color-text-disabled | 灰底灰字、点不动 |
| 加载 | btn-primary-loading | color-bg + 转动的菊花图标 | 绿底 + 菊花 |
微信里最基本的"点一下有反应",背后就是 normal → pressed → normal 的一个状态环路;每种状态都做对了、命名规范了,整张界面才有秩序。
深度
取舍:状态要不要全做? 6 种状态不是每个组件都要做齐。hover 是桌面端专属,手机上手指没有"悬停";focus 是键盘/无障碍必需品,纯手机产品可弱化。按场景取舍:
| 平台 / 场景 | 必做 | 可选 |
|---|---|---|
| 手机 App | normal、pressed、disabled、loading | hover(无鼠标)、focus(弱化) |
| 桌面 Web | normal、hover、pressed、disabled、loading、focus | — |
| 表单输入框 | focus(描边)、disabled、normal | pressed |
| 不可交互的展示组件(如头像) | normal | 其余基本不用 |
铁律:先做"用户一定会碰到"的状态(normal / pressed / disabled),再按平台补 hover / focus / loading。 别给一个纯展示组件硬凑 6 个状态——那叫过度设计。
命名反模式:四种最坑的起法。
| # | 反模式 | 例子 | 问题 |
|---|---|---|---|
| 1 | 无规范、随手起 | greenBtnPressed、btn1、那个按钮 | 没说清"哪个组件 + 哪个状态",团队张冠李戴 |
| 2 | 把颜色写进名字 | btn-lightgreen-pressed | 一换色名字就作废;颜色该交给 Token,不进名字 |
| 3 | 状态混在组件名里 | pressedBtn、disabledMsg | 状态应是后缀(btn-primary-pressed),不是前缀,否则排序检索全乱 |
| 4 | 同一状态多种叫法 | 有人叫 active、有人叫 pressed、有人叫 down | 同一件事三种叫法,等于没规范 |
反模式共同点:让"名字"失去了语义和唯一性。命名规范的意义,就是保证全团队对同一个状态喊同一个名字。
边界:状态与命名管什么、不管什么。
- 状态管"看起来、能不能点",不管"布局":状态是组件自身的样式变化,不改变组件的位置、大小、层级——那还是 Auto-Layout(13)和组件事。
- 命名管"怎么称呼",不管"怎么画":
btn-primary-pressed只定义身份;它长什么样,由引用的 Token(颜色)和组件样式决定。 - 状态要延续到代码,不只图纸:设计稿里定义了 pressed 的样式,开发代码里也要有对应的
:active/:hover状态——否则"设计做了、代码没做",按下没反应。
误区
| # | 误解 | 正解 |
|---|---|---|
| 1 | 状态 = 颜色变化 | 状态还有禁用(能不能点)、聚焦(键盘可访问)、加载(在等什么);颜色只是最显眼的表现之一 |
| 2 | 命名随便,反正是内部的 | 命名即规范;btn-primary-pressed 让全团队、全工具、全代码喊同一个名字 |
| 3 | 6 种状态每个组件都要做齐 | hover 是桌面专属、focus 是无障碍必需品,按平台取舍;纯展示组件做 normal 即可 |
| 4 | 颜色写进名字里没毛病 | 一换色名字就作废;颜色应交给 Token,状态名只管身份 |
| 5 | 状态是图纸的事,开发不用管 | 设计稿定义了 pressed/hover,代码里也要有对应状态,否则按下没反应 |
练习
即时题
Q1:组件状态一共有哪几种?哪一种是"键盘/无障碍"需要的?
答案
normal(默认)、hover(悬停)、pressed(按下)、disabled(禁用)、loading(加载)、focus(聚焦)。focus 是键盘定位/屏幕阅读器需要的。Q2:为什么说"状态 = 颜色变化"是误区?
答案
状态不止变色,还有禁用(能不能点)、聚焦(键盘可访问)、加载(在等结果)。颜色只是状态最显眼的表现之一,不是状态本身。Q3:btn-primary-pressed 这个名字,每个部分分别代表什么?
答案
btn=组件(按钮),primary=变体(主样式),pressed=状态(按下)。命名 = 组件 + 变体 + 状态,用连字符串起。Q4(衔接题):组件状态名和 Token(23)是什么关系?
答案
状态名是"身份"(谁的哪个状态),Token 是"颜色数据源"。`btn-primary-pressed` 引用 `color-bg-pressed` 这个语义 Token,改色只改 Token。Q5(一反模式):把状态名起成 greenBtnPressed,错在哪?
答案
①无规范、随手起,大小写混用;②把颜色(green)写进名字,一换色就作废;③状态放中间,检索和排序乱。应写成 `btn-primary-pressed`。模块题 微信新增一个"点赞"按钮。请:①列出它至少需要的 3 个状态;②分别给每个状态起规范名;③说明每个状态引用了哪个语义 Token(用 23 的语言)。
参考思路
- 状态:normal(默认)、pressed(按下)、disabled(禁用,如已点过或未登录)。手机上可省 hover。 - 命名:`btn-like-normal`(或省略为 `btn-like`)、`btn-like-pressed`、`btn-like-disabled`。 - Token:normal 用 `color-bg` + `color-text-inverse`;pressed 用 `color-bg-pressed`;disabled 用 `color-bg-disabled` + `color-text-disabled`。 - 关键点:**名字定身份、Token 定颜色**,状态集 + 命名规范 + Token 三件事各司其职,按钮才"可维护"。迷你案例(微信实战) 给微信的"发送"键做完一套"状态 + 命名 + Token"规范:
- 在你的微信设计文件里,找到那个绿色"发送"键组件。
- 给它定义状态:先做 normal / pressed / disabled 三个(手机上 hover 可省)。
- 给每个状态起规范名:
btn-primary、btn-primary-pressed、btn-primary-disabled。 - 为每个状态定颜色,并引用语义 Token(用 23 的语言):normal →
color-bg(品牌绿);pressed →color-bg-pressed(深一点);disabled →color-bg-disabled(灰)。 - 验证:把"发送框"清空,让"发送"键进入 disabled 态,看它是不是变灰、点不动。
- 最后在心里回答:如果老板说"按下态再深一点",你要改的是名字还是 Token?——是
color-bg-pressed这个 Token,btn-primary-pressed的名字不用动。
做完你就懂了:组件不是"一个长相",而是一套有状态、有名字、有 Token 数据源的规范。 状态和命名,就是设计系统"可维护"的秩序——它让团队从"那个绿了吧唧的按钮"升级成"btn-primary-pressed"。
素材
- 👉 打开交互演示:组件状态 — 同一按钮切换 normal/hover/pressed/disabled/loading/focus,对照规范名与引用的语义 Token。
- Material Design 状态:Google 定义的标准组件状态表(hover/focus/pressed/disabled/loading),看专业系统怎么定状态集。
- iOS HIG 状态:苹果对控件状态(enabled/disabled/selected)的规范,对照理解"状态集"怎么取舍。
- Design Tokens(23)衔接:本节把 23 的语义 Token 接到组件状态上——状态名引用 Token,颜色交给 Token 管。
- 后续衔接:24 组件状态与命名 → 组件库治理(把状态做全、名字统一)→ 设计系统整体规范交付。