设计规范
交互
状态模型、点击区域、响应时序、反馈层级、危险操作、隐藏与禁用、表单、键盘、提示、批量、拖拽、复制、导航与触控手势。
输入方式
指针、触控、键盘同等对待。悬停效果只写在 @media (hover: hover) 里;触控设备不依赖悬停显示任何信息(悬停出现的勾选框、把手在触控下常显)。
状态模型
每个可交互元素都要定义下列状态。
| 状态 | 光学含义 | 表现 | 时间 |
|---|---|---|---|
| 静止 rest | 未受光 | 规范外观 | — |
| 悬停 hover | 受光 | 卡片 +1 层、上移 1px、指光;控件叠 --state-hover(ink 5%,暗 7%);主按钮 → brand-hover | 淡变 150ms |
| 按下 pressed | 贴近台面 | 缩放 0.97(卡片 0.99)、−1 层、叠 --state-press(ink 9%,暗 12%) | 利落 |
| 聚焦 focus-visible | 光束 | 2px brand 焦点环,外移 2px,从外侧收拢到位;只在键盘聚焦时出现 | 利落 |
| 选中 selected | 光穿过 | brand-tint 底 + on-brand-tint 字 + 勾;从点击点“折射”展开 | 利落 |
| 当前 current | 被照亮的板 | 侧栏当前项:surface + elev-1 + brand 图标 + 字重 600 | — |
| 拖起 dragged | 举向光源 | 3 层、缩放 1.02、光标 grabbing | 平稳 |
| 禁用 disabled | 不透光 | 不透明度 0.40;无悬停与按下;cursor: not-allowed;必须能看到原因(提示) | — |
| 处理中 loading | 光在扫 | 300ms 后才显示转圈;一旦显示至少 400ms;按钮宽度不变、文字隐藏 | — |
| 有误 invalid | 红色色带 | danger 2px 环 + 字段下方说明 + 图标 | 淡变 |
| 只读 read-only | 透明 | 无容器底,文字 ink-2 | — |
点击区域
| 场景 | 最小 |
|---|---|
| 指针 | 32 × 32 |
| 触控 | 44 × 44(视觉更小的元素用透明扩展区) |
| 相邻目标间距 | 8 |
响应时序
| 耗时 | 反馈 |
|---|---|
| < 100ms | 直接反馈(状态切换),不出现加载提示 |
| 100ms – 1s | 前 300ms 不提示;之后在触发处显示行内转圈,至少显示 400ms,避免闪烁 |
| 1 – 10s | 首次加载用骨架(光扫);进度可知时用进度条 |
| > 10s | 转后台任务:吐司“已转入后台” + 任务中心 + 完成后站内通知 |
可逆的小操作(开关、标为已读、排序、收藏)乐观更新:立即改界面,失败时回滚并用吐司说明原因。
反馈层级
由轻到重:字段内(校验说明)< 状态文字(“已保存 10:32”)< 页面横幅(配额、公告、权限)< 吐司(操作结果,自动消失)< 对话框(需要决定,阻断)。选最轻的一层能说清的那一种;同一件事只用一层。
危险操作
| 类型 | 做法 |
|---|---|
| 可撤销(删除草稿、移出分组、归档) | 直接执行 + 吐司带「撤销」,6s,指针悬停时暂停计时 |
| 不可撤销(永久删除文章、撤销授权) | 确认对话框:标题写明对象名;正文说后果;危险按钮在右,取消在左 |
| 高影响(删除站点、删除公司、清空站群) | 确认对话框 + 输入对象名称后危险按钮才可用 |
隐藏还是禁用
| 情况 | 做法 |
|---|---|
| 没有权限 | 隐藏(useCan()) |
| 暂时不可用(配额用尽、状态不允许) | 禁用 + 提示说明原因与解决办法 |
| 表单未填完或校验未过 | 不禁用提交按钮;点击后显示错误并聚焦第一个错误字段 |
表单
- 字段失焦且内容改过才校验;出错后随输入实时复核,改对立即消除错误。
- 提交时校验全部字段,滚动并聚焦到第一个错误。
- 错误说明在字段下方,写明怎么改(“域名不能包含空格”),不写“格式错误”。
- 有未保存改动时,底部保存栏(磨砂、3 层)升起:“有未保存的更改 [放弃] [保存]”;离开页面前确认。
- 编辑器 2s 防抖自动保存,顶栏状态:“保存中…” → “已保存 10:32”;失败时状态变为 danger 并给出重试。
键盘
| 范围 | 按键 | 作用 |
|---|---|---|
| 全局 | ⌘K / Ctrl K | 搜索与命令面板 |
| 全局 | / | 聚焦当前列表的搜索框 |
| 全局 | Esc | 关闭最上层(提示 → 菜单 → 对话框 → 抽屉) |
| 全局 | ? | 快捷键帮助 |
| 列表 | ↑ / ↓ | 移动当前行 |
| 列表 | Enter | 打开当前行 |
| 列表 | Space | 选中 / 取消选中当前行 |
| 列表 | Shift + 点击 | 区间选择 |
| 标签页 / 分段 | ← / → | 切换 |
| 菜单 | 首字母 | 跳到匹配项 |
| 对话框 | — | 打开后聚焦第一个字段;焦点锁定在对话框内;关闭后焦点回到触发元素 |
Tab 顺序与视觉顺序一致;跳过链接“跳到主内容”是每页第一个焦点。
提示(Tooltip)
- 只用于:纯图标按钮、被截断的文字、禁用原因。不放可交互内容。
- 指针停留 500ms 出现;300ms 内移到相邻目标立即出现;离开立即消失;键盘聚焦时立即出现;Esc 关闭。
- 反色面(
inverse底 +on-inverse字)、r-xs、caption 字号、最宽 260。
选择与批量
- 桌面:勾选框在行悬停时出现(已有选中项时常显);触控:常显。
- 选中 ≥ 1 项后,底部中间升起磨砂批量栏(3 层、
r-full):“已选 3 项 · [操作…] · 清除”;数量变化用跃动弹簧。 - 全选只选当前页,并在批量栏提示“选择全部 128 项”。
拖拽
- 悬停出现把手(
grip-vertical);触控长按 500ms 拖起。 - 拖起:3 层、缩放 1.02;其他项用平稳弹簧让位;落下位置用
brand-tint占位条表示。 - 靠近容器上下边缘 48px 内自动滚动。
- 键盘替代:行菜单「上移 / 下移 / 移到顶部」。
复制
点击复制后图标换成勾、文字“已复制”,1.5s 后还原;剪贴板不可用时选中文本并提示“按 ⌘C 复制”。
导航与 URL
- 筛选、排序、标签页、页码、打开的抽屉都写进 URL;刷新与分享后状态一致。
- 返回时恢复滚动位置与筛选。
- 导航一律用真实链接(可在新标签打开);外链带
↗图标并在新标签打开。
触控手势(紧凑类别)
| 手势 | 阈值 | 结果 |
|---|---|---|
| 方向锁定 | 起手 8px | 判断横向 / 纵向后锁定,避免误触 |
| 左边缘右滑 | 起点距左边缘 ≤ 20px | 打开导航抽屉 |
| 抽屉 / 底部弹层关闭 | 位移 > 30%,或松手速度 > 0.5px/ms | 关闭;否则弹回(平稳) |
| 松手落点 | 按速度投影后的落点判断(减速率 0.998/ms) | 不看手指停在哪,看“会滑到哪” |
| 长按 | 500ms | 打开行菜单(或开始拖拽) |
| 下拉刷新 | 不使用 | 用页头刷新按钮 |
空、错、载入
| 状态 | 表现 |
|---|---|
| 首次载入 | 与最终布局同形的骨架(光扫),不用整页转圈 |
| 刷新 | 保留旧数据,只在触发处显示转圈;新数据交叉淡化替换 |
| 空(从未有数据) | 空模板:64 图标块 + 标题 + 说明 + 主操作 |
| 空(筛选无结果) | 一句话 + 「清除筛选」,不用大图标 |
| 出错 | 卡片内 danger 图标块 + 原因 + 「重试」;整页出错才用整页提示 |
| 无权限 | 说明需要的角色与联系人,不显示 403 代码 |