加载中和没有数据,是两件事
视觉上它们都是"一片空白",但含义完全相反。前者是"在动",后者是"没东西"。 混成一个,用户会以为系统坏了 —— 而他可能只是等了三秒。
切换时注意两件事:只有数据行在动,页头和工具条不动(布局稳定); 从"出错"点回"有数据",内容出现的位置正好是它原来的位置。
每态都必须给出口
只描述现状不给动作,等于把用户卡死在那儿。这是四态里最容易漏的一条。
空 —— 必须说明为什么是空的,并给下一步。 "还没有导入任何公司" 比 "暂无数据" 有用得多:前者告诉用户他在哪、缺什么。
出错 —— 必须能恢复。只写"出错了"是死路,而且刷新往往解决不了问题 (参数错了、文件格式不对)。要告诉用户哪里错了、怎么改。
成功 —— 是过渡态不是停留态。普通操作的成功反馈用按钮的
data-state="success"(1.2 秒自动消失),别做成一个整页。
骨架屏还是 spinner:看结构是否已知
知道会来"标题 + 三行 + 一张图",就照这个结构画占位。 价值不在好看,在于内容到位时不跳动。
🔴 别在结构未知时用骨架屏。画出来的结构跟实际不一致, 内容到位时布局会剧烈跳动 —— 那是在撒谎。
列表项的骨架:头像占位 + 两行文字
skeleton--avatar 是 40×40 的全圆占位,专门配 skeleton--text
做「头像 + 姓名 + 副标题」这种列表项。宽度节奏要跟真内容一致,
否则内容到位时头像会把文字挤走 —— 那正是骨架屏要避免的事。
300ms:不显示比闪一下好
请求在 300ms 内返回时不要显示加载态。骨架屏闪一下比不显示更糟 —— 那是一次无意义的闪烁,反而让人觉得卡。正确做法是延迟 300ms 再显示(约 10 行 JS)。
reduced-motion 下,两个动画处理相反
同样是"加载中的动画",一个保留一个去掉。判断依据是去掉后是否损失信息:
spinner 保留但放慢 —— 它停了用户会以为页面卡死。 reduced-motion 针对的是大幅位移(前庭功能障碍),小幅度原地旋转不属于此类。
骨架屏扫光停止 —— 静态占位块仍然传达"这里有内容正在来", 不损失信息;而扫光恰好是横向位移,正是要减少的那类。
对比:错写法 vs 本库写法
❌ 常见写法:empty 态没有出口
说明写得很清楚,然后呢?用户读完只剩一个动作:刷新,或者退出。一条"没有数据"的提示不构成一个状态,它只是死路。
✅ 本库:empty 态必须带动作
state__action 是 empty 态的一部分:要么给一条能走的路,要么别显示这个空状态。