UX质量检查 是做什么的?
根据 references/rules.md 中的规则检查变更所涉及的UI表面。每条规则包含了理由、代码检查方法、浏览器检查方法以及修复方法。该文件顶部的表格说明了每条规则适用于哪些类型的变更。
1. 找到变更
- 获取请求所指定的差异:一个PR、一个分支、一个提交或未提交的变更。若未指定,则使用未提交的变更。如果没有未提交的变更,则使用当前分支相对于其合并基础分支(默认分支)的差异(
git diff $(git merge-base HEAD origin/HEAD)...HEAD)。如果当前分支就是默认分支,则使用最后一个提交。在报告中说明检查的是哪个差异。 - 列出已变更的UI文件:组件、模板、样式,以及它们所渲染的字符串和属性。对共享组件或设计令牌的更改也属于UI更改。
- 如果差异中没有文件渲染UI(只有服务器代码、测试、配置或文档),则用一两句话说明并停止。报告无发现。
2. 阅读仓库自身的UI规则,并映射UI表面
仓库的规则优先于目录的默认规则。
- 阅读仓库的代理文档(
AGENTS.md,CLAUDE.md,设计系统README),查找“UI规则”或类似部分。注意其中为每条规则指定的控件、令牌或组件,例如对话框助手、异步按钮、类型令牌或AI标记。修复应指明该组件。 - 查找仓库自身的UX检查:
make ux-check或ux:check目标、布局或无障碍脚本、lint规则,或扫描源代码以应用UI规则的测试。运行每个覆盖此变更的检查,并使用其输出作为证据。
然后,将每个变更的文件映射到用户看到的UI表面:路由、视图和控件。一个共享组件会映射到使用它的每个表面。以用户发现该表面的方式命名(例如,“评论审核页面,添加评论表单”)。对于每个表面,需注意:
- 种类。 使用
references/rules.md的图例中的种类标记该表面,这些种类是由差异添加或修改的:write,data,form,list,control,overlay,layout,route,copy,chart,style。一个新的悬停样式既是control也是list。一个保存按钮是write。 - 状态。 空、加载中、待处理、错误、悬停、聚焦、选中和禁用。大多数发现都在作者未打开的状态中。
3. 运行检查
对于每个表面,运行其“适用类型”与该表面具有共同种类相匹配的每一条规则。在检查之前阅读规则的说明。记录每条跳过的规则及原因。
首先阅读代码。 每条规则的“代码检查”部分说明了要查找的内容。这能捕获大多数缺陷,且无需浏览器。在报告缺失状态之前,请阅读代码调用的组件:一个包装器(例如,异步按钮、表单钩子、传递 pending 属性的父组件)通常会处理该状态。
然后在浏览器中进行测量(如果可能)。 静态HTML页面可以通过 file:// URL从磁盘打开。对于应用程序,请查找正在运行的开发服务器,或使用仓库文档中记录的命令启动一个。使用宿主提供的浏览器工具:内置的浏览器面板、Playwright脚本或无头Chrome。如果页面要求登录,请要求用户在该浏览器中登录。不要自己输入任何凭据。
将 scripts/measure.js 加载到页面中。它定义了每条规则的“浏览器检查”部分所引用的辅助函数,references/rules.md 列出了每个函数返回的内容。每次页面加载后重新粘贴。检查应用程序提供的每个主题和配色方案,以及至少一个手机宽度和一个桌面宽度。
同时测量每个已变更的行、卡片和控件在其交互状态下的情况。 悬停、聚焦或选中状态通常会添加填充、边框、阴影或控件。这可能导致两个盒状项目相互接触,一个项目增长并移动其相邻项目,或者一个仅在悬停时显示的操作将计数推出对齐。对于该表面上的每个列表、卡片组或控件组:
- 在项目静止时运行
snapshot(sel, "rest")和gaps(sel)。 - 使用浏览器工具悬停一个项目,然后再次运行
snapshot(sel, "hover")和gaps(sel)。对键盘聚焦(按Tab键)以及将选中的项目放在悬停项目旁边时执行相同操作。 - 运行
moved("rest", "hover")。发现两个盒状项目之间的间隙为0px,或者当另一个项目被悬停时某个项目发生了移动,这都属于发现。
对打开菜单、弹出框或面板的每个操作执行相同操作:对主要块进行快照,打开它,再次快照,然后进行比较。在代码中,读取每个添加框或更改大小的属性的 :hover、:focus-visible 和选中类规则:background、border、box-shadow、padding、margin、width 和 display。
如果没有浏览器或没有可打开的页面,则报告代码发现并列出未执行的浏览器检查。
4. 验证每个发现
在报告发现之前,再次验证:
- 代码发现。 确认用户可以到达该状态:分支正在运行,属性已传递,并且没有包装器或父组件处理它。
- 浏览器发现。 重新加载页面后,在发现命名的状态下再次测量。丢弃未重复出现的值。
- 属于本次变更还是变更之前。 差异添加或修改的行上的缺陷属于本次变更。同样,差异添加了一个状态但没有添加其对应状态也属于本次变更,即使缺失的行存在于其他地方:例如,一个新的
:hover规则没有对应的:focus-visible规则,或一个新的写操作没有待处理状态。差异未触及的行上的缺陷在变更之前就已存在。将其报告在“现有缺陷”部分,与变更的发现分开,并仅当它位于已变更的表面上时才报告。
丢弃未通过验证的发现。将无法验证的发现报告为“未确认”,并说明原因。
5. 报告发现
首先用一行命名检查的差异和表面。然后,按缺陷严重程度从高到低给出每个发现。严重程度:
- 高:用户丢失数据或输入、发送重复写入、无法到达某个控件或无法阅读文本。
- 中:控件具有误导性(图标错误、仅靠颜色区分状态、缺少AI标记)或布局明显损坏。
- 低:与仓库系统不一致(使用原始类型值、混合选择控件、填充内容)。
每个发现包含四个部分:
| 部分 | 内容 |
|---|---|
| 表面 | 视图和控件,加上文件和行号(例如 ReviewForm.tsx:42) |
| 规则 | 目录中的规则ID,或它违反的仓库规则 |
| 证据 | 你读取或测量的内容:代码行、测量值、状态、主题和宽度 |
| 修复 | 用一两句话描述更改,如果仓库有命名的组件或令牌,则指明它们 |
示例:
表面: 面板侧边栏,作者过滤器列表(
AuthorList.tsx:58) 规则:no-cramped-controls证据:gaps(".author-row")在1440px宽度下,第2行悬停时:第1行(选中)和第2行之间的y: 0。静止时间隙也是0,但只有选中的行绘制了框。 修复: 为列表添加gap: 2px,或者将悬停填充向内偏移2px。
然后添加以下部分:
- 现有缺陷:步骤4中确定的、不是由本次变更引入的缺陷,每行一个。
- 未检查:跳过的规则及原因,以及未执行的浏览器检查。
当用户要求修复时,进行修复,然后对相同的表面再次运行相同的检查。