iter(g255): 减少动效下归零全站悬停位移
This commit is contained in:
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "ui-arch-registry-index/0.1",
|
"$schema": "ui-arch-registry-index/0.1",
|
||||||
"generatedAt": "2026-10-26T14:14:20.015Z",
|
"generatedAt": "2026-10-26T14:17:04.411Z",
|
||||||
"repoRoot": ".",
|
"repoRoot": ".",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
@@ -4123,7 +4123,7 @@
|
|||||||
"ua commit-lint <文件|message>:提交信息门禁(type 枚举 / scope 规则 / subject 中文祈使 ≤50 字符),规范见 COMMITTING.md,违规退出码 1",
|
"ua commit-lint <文件|message>:提交信息门禁(type 枚举 / scope 规则 / subject 中文祈使 ≤50 字符),规范见 COMMITTING.md,违规退出码 1",
|
||||||
"ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里各类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环/内距/控件高度/字重/行高/层级/边框色)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数与每类命中行号(line / prop / value / hint,Agent 可直接定位)",
|
"ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里各类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环/内距/控件高度/字重/行高/层级/边框色)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数与每类命中行号(line / prop / value / hint,Agent 可直接定位)",
|
||||||
"ua audit:contrast [--json|--list]:对比度门禁,把令牌里 16 组「文字色 × 底色」配对按 WCAG 公式算成对比度(求值支持 var() 与 color-mix,半透明底先压到页面底色),亮/暗 × 常规/高对比四套分算,低于阈值(正文 7 / 常规 4.5 / 禁用 2.6)退出码 1;--json 出逐对明细,--list 只列配对表",
|
"ua audit:contrast [--json|--list]:对比度门禁,把令牌里 16 组「文字色 × 底色」配对按 WCAG 公式算成对比度(求值支持 var() 与 color-mix,半透明底先压到页面底色),亮/暗 × 常规/高对比四套分算,低于阈值(正文 7 / 常规 4.5 / 禁用 2.6)退出码 1;--json 出逐对明细,--list 只列配对表",
|
||||||
"ua audit:motion [--json]:动效兜底门禁,扫描皮肤样式表里带 infinite 的动画,逐个核对是否在 prefers-reduced-motion 块里 animation: none,缺兜底退出码 1(--json 出逐条明细)",
|
"ua audit:motion [--json]:动效兜底门禁,两类必查——① 带 infinite 的动画逐个核对是否在 prefers-reduced-motion 块里 animation: none;② 悬停 / 按压的位移(translate)是否在减少动效下归零(认 ua- 类通配的全局规则)。缺兜底退出码 1(--json 出逐条明细)",
|
||||||
"ua mcp:零依赖 stdio MCP server,暴露 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 支持 format: md 直接取可粘贴的 Markdown 片段"
|
"ua mcp:零依赖 stdio MCP server,暴露 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 支持 format: md 直接取可粘贴的 Markdown 片段"
|
||||||
],
|
],
|
||||||
"keyTypes": [
|
"keyTypes": [
|
||||||
@@ -4148,7 +4148,7 @@
|
|||||||
"改完清单跑 npm run docs:同时刷新人类文档站与 llms-full.txt(Agent 首选喂料)",
|
"改完清单跑 npm run docs:同时刷新人类文档站与 llms-full.txt(Agent 首选喂料)",
|
||||||
"改完样式跑 npm run audit:visual:各类硬编码计数不得高于 .ua/visual-baseline.json(棘轮只减不增);字面量可用 npm run visual:migrate 幂等改写成令牌",
|
"改完样式跑 npm run audit:visual:各类硬编码计数不得高于 .ua/visual-baseline.json(棘轮只减不增);字面量可用 npm run visual:migrate 幂等改写成令牌",
|
||||||
"改完颜色令牌跑 npm run audit:contrast:16 组文字/底色配对在亮/暗 × 常规/高对比四套主题下都必须达标(正文 7 / 常规 4.5 / 禁用 2.6),跌破即退出码 1——调一个色值就能让全站辅助文字掉到 4.4,人眼看不出但门禁看得出",
|
"改完颜色令牌跑 npm run audit:contrast:16 组文字/底色配对在亮/暗 × 常规/高对比四套主题下都必须达标(正文 7 / 常规 4.5 / 禁用 2.6),跌破即退出码 1——调一个色值就能让全站辅助文字掉到 4.4,人眼看不出但门禁看得出",
|
||||||
"改完动效跑 npm run audit:motion:带 infinite 的动画必须在 prefers-reduced-motion 块里被关掉,缺一条即退出码 1——循环动画漏兜底会把「减少动效」用户整页交给抖动",
|
"改完动效跑 npm run audit:motion:带 infinite 的动画必须在 prefers-reduced-motion 块里被关掉,悬停 / 按压的位移(translate)必须归零,缺一条即退出码 1——循环动画漏兜底会把「减少动效」用户整页交给抖动,位移漏兜底则每次悬停都在推搡前庭",
|
||||||
"在 ~/.workbuddy/mcp.json 注册 ua mcp 后,Agent 可直接调 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 传 format: md 拿到与 ua info --md 同款的 Markdown 片段"
|
"在 ~/.workbuddy/mcp.json 注册 ua mcp 后,Agent 可直接调 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 传 format: md 拿到与 ua info --md 同款的 Markdown 片段"
|
||||||
],
|
],
|
||||||
"components": []
|
"components": []
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
- `apps/demo`(@ui-arch/demo):交互演示,产物 apps/demo/dist/demo.html
|
- `apps/demo`(@ui-arch/demo):交互演示,产物 apps/demo/dist/demo.html
|
||||||
- `apps/docs`:文档站,纯产物(ua docs 生成)
|
- `apps/docs`:文档站,纯产物(ua docs 生成)
|
||||||
|
|
||||||
**门禁一条龙**:`npm run check` = build + demo + style + typecheck + smoke + index + verify + lint-iterations + audit:visual + audit:contrast + audit:motion,提交前必过。其中 `smoke` 是两段内核断言:`@ui-arch/frame` 14 条(信号读写与同值短路、effect 注销与条件分支重追踪、computed 链式派生、多订阅者扇出)+ `@ui-arch/table-core` 26 条(排序 / 筛选 / 分页 / 选择的派生管线 + setRows 换源与失效行剔除);改内核语义前先跑它,别靠组件 demo 肉眼验。`ua audit:visual` 统计皮肤包里十四类硬编码视觉字面量(字号 / 动效时长 / 阴影 / 圆角 / 焦点环 / 内距 / 控件高度 / 字重 / 行高 / 层级 / 边框色 / 混色浓度 / transform 位移 / 焦点描边)并与 `.ua/visual-baseline.json` 做棘轮:任一类超过基线即退出码 1 并指出疑似引入的提交;迁移脚本 `npm run visual:migrate` 可把字面量幂等改写成令牌。`ua audit:contrast` 走另一条线:把令牌里 16 组「文字色 × 底色」配对(正文/次级/辅助文字 × 页面底/表面/凹陷底、强调色与其反白、语义三色、禁用态豁免档)按 WCAG 公式算成对比度(支持 `color-mix` 与 `var()` 求值,半透明底先压到页面底色上),亮 / 暗 × 常规 / 高对比四套主题各算一遍,低于阈值(正文 7 / 常规 4.5 / 禁用 2.6)即退出码 1;`--json` 出逐对明细,`--list` 只列配对表。改任一颜色令牌后它会立刻告诉你「改完谁读不清了」,不必等人工目测暗色。`ua audit:motion` 守的是动效面:扫描皮肤样式表里所有带 `infinite` 的动画,逐个核对它是否在 `prefers-reduced-motion: reduce` 块里被 `animation: none` 关掉——循环动画是前庭敏感用户的硬伤,漏一条整页就在抖(本轮就靠它抓出转圈与不确定态进度两处漏网)。`ua verify` 校验清单 ↔ 文档 ↔ 演示一致(docs.components 的 id 必须在 api.exports 有对应条目、props 必须带 n/t/d、每个文档字段名必须在组件实现源码里真实出现(改名后忘改文档会直接报错)、反向也校验 `src/types.ts` 里 `<组件>Props`(可用清单的 `propsType` 覆盖接口名,`false` 表示无入参)每个字段都在文档 props 表登记(新加字段忘了写文档会报错)、demo 锚点必须在 `apps/demo/main.ts` 中存在,且该组件必须被 demo 真实调用 `id(` / `id.`),检查皮肤包样式表无硬编码颜色(`#hex` 一律禁止,必须走 `--ua-*` 令牌)与令牌引用有效(`var(--ua-*)` 必须是 tokens.css 已定义或组件自设的变量)、核对每个 `--ua-color-*` 都在 `[data-theme='dark']` 里有覆盖值且暗色块没有凭空多出的变量名(新增颜色令牌漏写暗色值直接报错)、核对 `tokens.json` 的 `color` 键与 tokens.css 的 `--ua-color-*` 同名同值(名字按 camelCase → kebab 折算,亮色值与暗色值都比,双向查漏——只改一处时文档色板会凭空多/少一个令牌,过去只能靠人眼发现)、核对 `@keyframes` 没有「帧体完全相同但名字不同」的复制粘贴(同一段动效只该有一份声明,改名复制会让方向 / 时长各写各的,翻转态就出现过「朝反方向入场」),核对 README 的 `<!-- ua:components -->` 区块与「分组:」行都覆盖清单组件集合(漏写/多写/重复都报错),并交叉校验 backlog 的 done 条目与 `.ua/iterations.log` 的 done 留痕一一对应(另外查 backlog id 不重复、g 编号严格递增、同一 id 只有一条 done 留痕,且留痕 ts 带 +08:00、时间线不回退、不落在未来——都是过去手工插条目/手写时间戳踩过的坑);配方还要求编号连续、每条都有代码块与要点,且代码块里至少认出一个已注册组件 id(拼错组件名时 `ua recipes` 会把它列成空组件表)。
|
**门禁一条龙**:`npm run check` = build + demo + style + typecheck + smoke + index + verify + lint-iterations + audit:visual + audit:contrast + audit:motion,提交前必过。其中 `smoke` 是两段内核断言:`@ui-arch/frame` 14 条(信号读写与同值短路、effect 注销与条件分支重追踪、computed 链式派生、多订阅者扇出)+ `@ui-arch/table-core` 31 条(排序 / 筛选 / 分页 / 选择的派生管线 + setRows 换源与失效行剔除 + 越界页码回写);改内核语义前先跑它,别靠组件 demo 肉眼验。`ua audit:visual` 统计皮肤包里十四类硬编码视觉字面量(字号 / 动效时长 / 阴影 / 圆角 / 焦点环 / 内距 / 控件高度 / 字重 / 行高 / 层级 / 边框色 / 混色浓度 / transform 位移 / 焦点描边)并与 `.ua/visual-baseline.json` 做棘轮:任一类超过基线即退出码 1 并指出疑似引入的提交;迁移脚本 `npm run visual:migrate` 可把字面量幂等改写成令牌。`ua audit:contrast` 走另一条线:把令牌里 16 组「文字色 × 底色」配对(正文/次级/辅助文字 × 页面底/表面/凹陷底、强调色与其反白、语义三色、禁用态豁免档)按 WCAG 公式算成对比度(支持 `color-mix` 与 `var()` 求值,半透明底先压到页面底色上),亮 / 暗 × 常规 / 高对比四套主题各算一遍,低于阈值(正文 7 / 常规 4.5 / 禁用 2.6)即退出码 1;`--json` 出逐对明细,`--list` 只列配对表。改任一颜色令牌后它会立刻告诉你「改完谁读不清了」,不必等人工目测暗色。`ua audit:motion` 守的是动效面,两类必查:① 带 `infinite` 的动画是否在 `prefers-reduced-motion: reduce` 块里被 `animation: none` 关掉——循环动画是前庭敏感用户的硬伤,漏一条整页就在抖(它抓出过转圈与不确定态进度两处漏网);② 悬停 / 按压的位移(`translate`)是否在减少动效下归零——位移最容易诱发前庭不适,且只提供手感、不表达状态(一条 `ua-` 类通配的 `transform: none !important` 兜住全站 14 处,新增组件不用各自补)。`ua verify` 校验清单 ↔ 文档 ↔ 演示一致(docs.components 的 id 必须在 api.exports 有对应条目、props 必须带 n/t/d、每个文档字段名必须在组件实现源码里真实出现(改名后忘改文档会直接报错)、反向也校验 `src/types.ts` 里 `<组件>Props`(可用清单的 `propsType` 覆盖接口名,`false` 表示无入参)每个字段都在文档 props 表登记(新加字段忘了写文档会报错)、demo 锚点必须在 `apps/demo/main.ts` 中存在,且该组件必须被 demo 真实调用 `id(` / `id.`),检查皮肤包样式表无硬编码颜色(`#hex` 一律禁止,必须走 `--ua-*` 令牌)与令牌引用有效(`var(--ua-*)` 必须是 tokens.css 已定义或组件自设的变量)、核对每个 `--ua-color-*` 都在 `[data-theme='dark']` 里有覆盖值且暗色块没有凭空多出的变量名(新增颜色令牌漏写暗色值直接报错)、核对 `tokens.json` 的 `color` 键与 tokens.css 的 `--ua-color-*` 同名同值(名字按 camelCase → kebab 折算,亮色值与暗色值都比,双向查漏——只改一处时文档色板会凭空多/少一个令牌,过去只能靠人眼发现)、核对 `@keyframes` 没有「帧体完全相同但名字不同」的复制粘贴(同一段动效只该有一份声明,改名复制会让方向 / 时长各写各的,翻转态就出现过「朝反方向入场」),核对 README 的 `<!-- ua:components -->` 区块与「分组:」行都覆盖清单组件集合(漏写/多写/重复都报错),并交叉校验 backlog 的 done 条目与 `.ua/iterations.log` 的 done 留痕一一对应(另外查 backlog id 不重复、g 编号严格递增、同一 id 只有一条 done 留痕,且留痕 ts 带 +08:00、时间线不回退、不落在未来——都是过去手工插条目/手写时间戳踩过的坑);配方还要求编号连续、每条都有代码块与要点,且代码块里至少认出一个已注册组件 id(拼错组件名时 `ua recipes` 会把它列成空组件表)。
|
||||||
|
|
||||||
`ua verify` 另外校验 `ITERATION.md` 的《视觉回归抽查清单》:button / textField / select / modal / dataTable / toast 六段各 ≥3 条可勾选要点、每条维度标签必须落在边框 / 阴影 / 过渡 / 焦点环 / 暗色五类内、合计 ≥18 条且五维度全覆盖——视觉类条目收尾前照单过一遍,改清单时别把条目删漏。
|
`ua verify` 另外校验 `ITERATION.md` 的《视觉回归抽查清单》:button / textField / select / modal / dataTable / toast 六段各 ≥3 条可勾选要点、每条维度标签必须落在边框 / 阴影 / 过渡 / 焦点环 / 暗色五类内、合计 ≥18 条且五维度全覆盖——视觉类条目收尾前照单过一遍,改清单时别把条目删漏。
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
# @ui-arch 组件库 Agent 文档(无图形版)
|
# @ui-arch 组件库 Agent 文档(无图形版)
|
||||||
|
|
||||||
> 自动生成于 2026-10-26T14:14:20.015Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
> 自动生成于 2026-10-26T14:17:04.411Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||||
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
||||||
|
|
||||||
## 快速上手
|
## 快速上手
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
6. 改完清单跑 npm run docs:同时刷新人类文档站与 llms-full.txt(Agent 首选喂料)
|
6. 改完清单跑 npm run docs:同时刷新人类文档站与 llms-full.txt(Agent 首选喂料)
|
||||||
7. 改完样式跑 npm run audit:visual:各类硬编码计数不得高于 .ua/visual-baseline.json(棘轮只减不增);字面量可用 npm run visual:migrate 幂等改写成令牌
|
7. 改完样式跑 npm run audit:visual:各类硬编码计数不得高于 .ua/visual-baseline.json(棘轮只减不增);字面量可用 npm run visual:migrate 幂等改写成令牌
|
||||||
8. 改完颜色令牌跑 npm run audit:contrast:16 组文字/底色配对在亮/暗 × 常规/高对比四套主题下都必须达标(正文 7 / 常规 4.5 / 禁用 2.6),跌破即退出码 1——调一个色值就能让全站辅助文字掉到 4.4,人眼看不出但门禁看得出
|
8. 改完颜色令牌跑 npm run audit:contrast:16 组文字/底色配对在亮/暗 × 常规/高对比四套主题下都必须达标(正文 7 / 常规 4.5 / 禁用 2.6),跌破即退出码 1——调一个色值就能让全站辅助文字掉到 4.4,人眼看不出但门禁看得出
|
||||||
9. 改完动效跑 npm run audit:motion:带 infinite 的动画必须在 prefers-reduced-motion 块里被关掉,缺一条即退出码 1——循环动画漏兜底会把「减少动效」用户整页交给抖动
|
9. 改完动效跑 npm run audit:motion:带 infinite 的动画必须在 prefers-reduced-motion 块里被关掉,悬停 / 按压的位移(translate)必须归零,缺一条即退出码 1——循环动画漏兜底会把「减少动效」用户整页交给抖动,位移漏兜底则每次悬停都在推搡前庭
|
||||||
10. 在 ~/.workbuddy/mcp.json 注册 ua mcp 后,Agent 可直接调 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 传 format: md 拿到与 ua info --md 同款的 Markdown 片段
|
10. 在 ~/.workbuddy/mcp.json 注册 ua mcp 后,Agent 可直接调 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 传 format: md 拿到与 ua info --md 同款的 Markdown 片段
|
||||||
|
|
||||||
## @ui-arch/ui(skin-dom)v0.1.0
|
## @ui-arch/ui(skin-dom)v0.1.0
|
||||||
@@ -2355,7 +2355,7 @@ API 速览:
|
|||||||
- ua commit-lint <文件|message>:提交信息门禁(type 枚举 / scope 规则 / subject 中文祈使 ≤50 字符),规范见 COMMITTING.md,违规退出码 1
|
- ua commit-lint <文件|message>:提交信息门禁(type 枚举 / scope 规则 / subject 中文祈使 ≤50 字符),规范见 COMMITTING.md,违规退出码 1
|
||||||
- ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里各类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环/内距/控件高度/字重/行高/层级/边框色)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数与每类命中行号(line / prop / value / hint,Agent 可直接定位)
|
- ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里各类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环/内距/控件高度/字重/行高/层级/边框色)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数与每类命中行号(line / prop / value / hint,Agent 可直接定位)
|
||||||
- ua audit:contrast [--json|--list]:对比度门禁,把令牌里 16 组「文字色 × 底色」配对按 WCAG 公式算成对比度(求值支持 var() 与 color-mix,半透明底先压到页面底色),亮/暗 × 常规/高对比四套分算,低于阈值(正文 7 / 常规 4.5 / 禁用 2.6)退出码 1;--json 出逐对明细,--list 只列配对表
|
- ua audit:contrast [--json|--list]:对比度门禁,把令牌里 16 组「文字色 × 底色」配对按 WCAG 公式算成对比度(求值支持 var() 与 color-mix,半透明底先压到页面底色),亮/暗 × 常规/高对比四套分算,低于阈值(正文 7 / 常规 4.5 / 禁用 2.6)退出码 1;--json 出逐对明细,--list 只列配对表
|
||||||
- ua audit:motion [--json]:动效兜底门禁,扫描皮肤样式表里带 infinite 的动画,逐个核对是否在 prefers-reduced-motion 块里 animation: none,缺兜底退出码 1(--json 出逐条明细)
|
- ua audit:motion [--json]:动效兜底门禁,两类必查——① 带 infinite 的动画逐个核对是否在 prefers-reduced-motion 块里 animation: none;② 悬停 / 按压的位移(translate)是否在减少动效下归零(认 ua- 类通配的全局规则)。缺兜底退出码 1(--json 出逐条明细)
|
||||||
- ua mcp:零依赖 stdio MCP server,暴露 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 支持 format: md 直接取可粘贴的 Markdown 片段
|
- ua mcp:零依赖 stdio MCP server,暴露 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 支持 format: md 直接取可粘贴的 Markdown 片段
|
||||||
|
|
||||||
一行用法:
|
一行用法:
|
||||||
|
|||||||
@@ -147,7 +147,7 @@
|
|||||||
- ua commit-lint <文件|message>:提交信息门禁(type 枚举 / scope 规则 / subject 中文祈使 ≤50 字符),规范见 COMMITTING.md,违规退出码 1
|
- ua commit-lint <文件|message>:提交信息门禁(type 枚举 / scope 规则 / subject 中文祈使 ≤50 字符),规范见 COMMITTING.md,违规退出码 1
|
||||||
- ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里各类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环/内距/控件高度/字重/行高/层级/边框色)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数与每类命中行号(line / prop / value / hint,Agent 可直接定位)
|
- ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里各类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环/内距/控件高度/字重/行高/层级/边框色)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数与每类命中行号(line / prop / value / hint,Agent 可直接定位)
|
||||||
- ua audit:contrast [--json|--list]:对比度门禁,把令牌里 16 组「文字色 × 底色」配对按 WCAG 公式算成对比度(求值支持 var() 与 color-mix,半透明底先压到页面底色),亮/暗 × 常规/高对比四套分算,低于阈值(正文 7 / 常规 4.5 / 禁用 2.6)退出码 1;--json 出逐对明细,--list 只列配对表
|
- ua audit:contrast [--json|--list]:对比度门禁,把令牌里 16 组「文字色 × 底色」配对按 WCAG 公式算成对比度(求值支持 var() 与 color-mix,半透明底先压到页面底色),亮/暗 × 常规/高对比四套分算,低于阈值(正文 7 / 常规 4.5 / 禁用 2.6)退出码 1;--json 出逐对明细,--list 只列配对表
|
||||||
- ua audit:motion [--json]:动效兜底门禁,扫描皮肤样式表里带 infinite 的动画,逐个核对是否在 prefers-reduced-motion 块里 animation: none,缺兜底退出码 1(--json 出逐条明细)
|
- ua audit:motion [--json]:动效兜底门禁,两类必查——① 带 infinite 的动画逐个核对是否在 prefers-reduced-motion 块里 animation: none;② 悬停 / 按压的位移(translate)是否在减少动效下归零(认 ua- 类通配的全局规则)。缺兜底退出码 1(--json 出逐条明细)
|
||||||
- ua mcp:零依赖 stdio MCP server,暴露 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 支持 format: md 直接取可粘贴的 Markdown 片段
|
- ua mcp:零依赖 stdio MCP server,暴露 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 支持 format: md 直接取可粘贴的 Markdown 片段
|
||||||
- 用法: `node packages/tools/cli/bin/ua.js <index|list|info <包名|组件id> [--json|--md]|docs|tokens [--json|--unused]|style|verify|commit-lint|audit:visual|audit:contrast|audit:motion|doctor|mcp>;仓库内等价于 npm run index / docs / style / verify / audit:visual / audit:contrast / audit:motion / doctor`
|
- 用法: `node packages/tools/cli/bin/ua.js <index|list|info <包名|组件id> [--json|--md]|docs|tokens [--json|--unused]|style|verify|commit-lint|audit:visual|audit:contrast|audit:motion|doctor|mcp>;仓库内等价于 npm run index / docs / style / verify / audit:visual / audit:contrast / audit:motion / doctor`
|
||||||
- 约定: 零依赖:只用 node 标准库,Node >= 18
|
- 约定: 零依赖:只用 node 标准库,Node >= 18
|
||||||
|
|||||||
@@ -5295,3 +5295,17 @@ h5.ua-type-title { font-size: var(--ua-font-size-md); }
|
|||||||
width: 100%; /* 不确定态降级为满宽淡条:不再左右扫,语义仍是「进行中」 */
|
width: 100%; /* 不确定态降级为满宽淡条:不再左右扫,语义仍是「进行中」 */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- 减少动效:悬停上浮 / 按压回缩一律归零 ----
|
||||||
|
位移是最容易诱发前庭不适的一类动效,且它只提供「手感」、不表达状态;
|
||||||
|
颜色 / 描边 / 阴影过渡保留,用户要减的是「动」,不是反馈。
|
||||||
|
用 ua- 类通配写一条全局规则,新增组件不再需要各自补降级(audit:motion 认这条作全量兜底)。 */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
[class^='ua-']:hover,
|
||||||
|
[class*=' ua-']:hover,
|
||||||
|
[class^='ua-']:active,
|
||||||
|
[class*=' ua-']:active {
|
||||||
|
transform: none !important; /* 上浮 / 缩放 / 回缩全部归零 */
|
||||||
|
translate: none !important; /* 少数组件用独立 translate 属性做位移,一并归零 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1712,12 +1712,13 @@ function cmdAuditMotion(args) {
|
|||||||
console.error(`audit:motion 失败:${err.message}`);
|
console.error(`audit:motion 失败:${err.message}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
const failing = result.gaps.length + result.shift.gaps.length; // 两类缺口都算失败:无限动画 + 悬停/按压位移
|
||||||
if (args.includes('--json')) {
|
if (args.includes('--json')) {
|
||||||
console.log(JSON.stringify({ ok: result.gaps.length === 0, total: result.total, failing: result.gaps.length, entries: result.entries }, null, 2));
|
console.log(JSON.stringify({ ok: failing === 0, total: result.total, failing, entries: result.entries, shift: result.shift }, null, 2));
|
||||||
} else {
|
} else {
|
||||||
console.log(formatMotionReport(result));
|
console.log(formatMotionReport(result));
|
||||||
}
|
}
|
||||||
process.exit(result.gaps.length === 0 ? 0 : 1);
|
process.exit(failing === 0 ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** 迭代留痕校验:.ua/iterations.log 每行 JSON 字段与格式必须符合 ITERATION.md 规范 ***/
|
/*** 迭代留痕校验:.ua/iterations.log 每行 JSON 字段与格式必须符合 ITERATION.md 规范 ***/
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
/*** @ui-arch 动效兜底审计:带 `infinite` 的动画必须在 prefers-reduced-motion 下被显式关掉
|
/*** @ui-arch 动效兜底审计:跑起来不停的和「一动就位移的」都必须在 prefers-reduced-motion 下被显式关掉
|
||||||
*
|
*
|
||||||
* 为什么单独成门禁:`audit:visual` 只管字面量、`verify` 只管「动效时长走令牌」,没人管「无限循环动画有没有降级」。
|
* 为什么单独成门禁:`audit:visual` 只管字面量、`verify` 只管「动效时长走令牌」,没人管「动效有没有降级」。
|
||||||
* 循环动画(转圈、扫光、进度条流纹、时间线脉冲)是前庭敏感用户的硬伤,漏一条就等于把整页交给抖动;
|
* 两类必查:① 无限循环动画(转圈、扫光、进度条流纹、时间线脉冲)——前庭敏感用户的硬伤,漏一条整页都在抖;
|
||||||
* 有限时长的过渡(hover 上浮之类)危害小得多,不在本门禁范围——只盯 infinite 才有明确可执行的判定。
|
* ② 悬停 / 按压位移(translate)——位移是最容易诱发不适的一类,纯装饰手感,减动效时必须归零。
|
||||||
|
* 只查这两类而不是所有过渡:颜色 / 阴影 / 透明度变化不产生空间位移,砍掉它们反而丢了状态反馈。
|
||||||
*/
|
*/
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
@@ -57,14 +58,30 @@ export function auditMotion(root) {
|
|||||||
.flatMap((r) => splitSelectors(r.selector)),
|
.flatMap((r) => splitSelectors(r.selector)),
|
||||||
);
|
);
|
||||||
const entries = infinite.flatMap((r) => splitSelectors(r.selector).map((selector) => ({ selector, covered: offline.has(selector) })));
|
const entries = infinite.flatMap((r) => splitSelectors(r.selector).map((selector) => ({ selector, covered: offline.has(selector) })));
|
||||||
|
const shift = auditShift(rules);
|
||||||
|
return { total: entries.length, entries, gaps: entries.filter((e) => !e.covered), shift };
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** 作用:悬停 / 按压位移的降级审计——逐个位移规则找对应降级,或以 ua- 通配写的全局归零规则兜底 ***/
|
||||||
|
function auditShift(rules) {
|
||||||
|
const isShift = (body) => /transform\s*:[^;]*translate|translate\s*:/.test(body); // 位移两种写法:transform 里的 translate / 独立的 translate 属性
|
||||||
|
const isState = (selector) => /:(hover|active)\b/.test(selector); // 只认交互态位移:布局位移(抽屉开合)不在降级范围
|
||||||
|
const nulled = rules.filter((r) => r.reduced && /transform\s*:\s*none|translate\s*:\s*none/.test(r.body));
|
||||||
|
const exact = new Set(nulled.flatMap((r) => splitSelectors(r.selector)));
|
||||||
|
const global = nulled.some((r) => isState(r.selector) && /\[class\^='?ua-'?\]|\[class\*='? ua-'?\]/.test(r.selector)); // 通配 ua- 类的全局规则视为覆盖全部组件
|
||||||
|
const entries = rules
|
||||||
|
.filter((r) => !r.reduced && isState(r.selector) && isShift(r.body))
|
||||||
|
.flatMap((r) => splitSelectors(r.selector).filter(isState).map((selector) => ({ selector, covered: global || exact.has(selector) })));
|
||||||
return { total: entries.length, entries, gaps: entries.filter((e) => !e.covered) };
|
return { total: entries.length, entries, gaps: entries.filter((e) => !e.covered) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** 作用:把审计结果打成文本表(人读模式) ***/
|
/*** 作用:把审计结果打成文本表(人读模式) ***/
|
||||||
export function formatMotionReport({ entries, gaps }) {
|
export function formatMotionReport({ entries, gaps, shift }) {
|
||||||
const lines = ['无限动画的减少动效兜底:'];
|
const lines = ['无限动画的减少动效兜底:'];
|
||||||
for (const e of entries) lines.push(` ${e.covered ? 'OK ' : 'MISS'} ${e.selector}`);
|
for (const e of entries) lines.push(` ${e.covered ? 'OK ' : 'MISS'} ${e.selector}`);
|
||||||
lines.push('');
|
|
||||||
lines.push(gaps.length ? `缺兜底 ${gaps.length} 处:${gaps.map((g) => g.selector).join(' / ')}` : `全部 ${entries.length} 处无限动画都有 prefers-reduced-motion 兜底`);
|
lines.push(gaps.length ? `缺兜底 ${gaps.length} 处:${gaps.map((g) => g.selector).join(' / ')}` : `全部 ${entries.length} 处无限动画都有 prefers-reduced-motion 兜底`);
|
||||||
|
lines.push('', '悬停 / 按压位移的减少动效兜底:');
|
||||||
|
const ok = shift.entries.filter((e) => e.covered).length;
|
||||||
|
lines.push(shift.gaps.length ? ` MISS 未归零 ${shift.gaps.length} 处:${shift.gaps.map((g) => g.selector).join(' / ')}` : ` 全部 ${ok} 处位移在减少动效下归零(悬停上浮 / 按压回缩)`);
|
||||||
return lines.join('\n');
|
return lines.join('\n');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
"ua commit-lint <文件|message>:提交信息门禁(type 枚举 / scope 规则 / subject 中文祈使 ≤50 字符),规范见 COMMITTING.md,违规退出码 1",
|
"ua commit-lint <文件|message>:提交信息门禁(type 枚举 / scope 规则 / subject 中文祈使 ≤50 字符),规范见 COMMITTING.md,违规退出码 1",
|
||||||
"ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里各类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环/内距/控件高度/字重/行高/层级/边框色)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数与每类命中行号(line / prop / value / hint,Agent 可直接定位)",
|
"ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里各类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环/内距/控件高度/字重/行高/层级/边框色)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数与每类命中行号(line / prop / value / hint,Agent 可直接定位)",
|
||||||
"ua audit:contrast [--json|--list]:对比度门禁,把令牌里 16 组「文字色 × 底色」配对按 WCAG 公式算成对比度(求值支持 var() 与 color-mix,半透明底先压到页面底色),亮/暗 × 常规/高对比四套分算,低于阈值(正文 7 / 常规 4.5 / 禁用 2.6)退出码 1;--json 出逐对明细,--list 只列配对表",
|
"ua audit:contrast [--json|--list]:对比度门禁,把令牌里 16 组「文字色 × 底色」配对按 WCAG 公式算成对比度(求值支持 var() 与 color-mix,半透明底先压到页面底色),亮/暗 × 常规/高对比四套分算,低于阈值(正文 7 / 常规 4.5 / 禁用 2.6)退出码 1;--json 出逐对明细,--list 只列配对表",
|
||||||
"ua audit:motion [--json]:动效兜底门禁,扫描皮肤样式表里带 infinite 的动画,逐个核对是否在 prefers-reduced-motion 块里 animation: none,缺兜底退出码 1(--json 出逐条明细)",
|
"ua audit:motion [--json]:动效兜底门禁,两类必查——① 带 infinite 的动画逐个核对是否在 prefers-reduced-motion 块里 animation: none;② 悬停 / 按压的位移(translate)是否在减少动效下归零(认 ua- 类通配的全局规则)。缺兜底退出码 1(--json 出逐条明细)",
|
||||||
"ua mcp:零依赖 stdio MCP server,暴露 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 支持 format: md 直接取可粘贴的 Markdown 片段"
|
"ua mcp:零依赖 stdio MCP server,暴露 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 支持 format: md 直接取可粘贴的 Markdown 片段"
|
||||||
],
|
],
|
||||||
"keyTypes": [
|
"keyTypes": [
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
"改完清单跑 npm run docs:同时刷新人类文档站与 llms-full.txt(Agent 首选喂料)",
|
"改完清单跑 npm run docs:同时刷新人类文档站与 llms-full.txt(Agent 首选喂料)",
|
||||||
"改完样式跑 npm run audit:visual:各类硬编码计数不得高于 .ua/visual-baseline.json(棘轮只减不增);字面量可用 npm run visual:migrate 幂等改写成令牌",
|
"改完样式跑 npm run audit:visual:各类硬编码计数不得高于 .ua/visual-baseline.json(棘轮只减不增);字面量可用 npm run visual:migrate 幂等改写成令牌",
|
||||||
"改完颜色令牌跑 npm run audit:contrast:16 组文字/底色配对在亮/暗 × 常规/高对比四套主题下都必须达标(正文 7 / 常规 4.5 / 禁用 2.6),跌破即退出码 1——调一个色值就能让全站辅助文字掉到 4.4,人眼看不出但门禁看得出",
|
"改完颜色令牌跑 npm run audit:contrast:16 组文字/底色配对在亮/暗 × 常规/高对比四套主题下都必须达标(正文 7 / 常规 4.5 / 禁用 2.6),跌破即退出码 1——调一个色值就能让全站辅助文字掉到 4.4,人眼看不出但门禁看得出",
|
||||||
"改完动效跑 npm run audit:motion:带 infinite 的动画必须在 prefers-reduced-motion 块里被关掉,缺一条即退出码 1——循环动画漏兜底会把「减少动效」用户整页交给抖动",
|
"改完动效跑 npm run audit:motion:带 infinite 的动画必须在 prefers-reduced-motion 块里被关掉,悬停 / 按压的位移(translate)必须归零,缺一条即退出码 1——循环动画漏兜底会把「减少动效」用户整页交给抖动,位移漏兜底则每次悬停都在推搡前庭",
|
||||||
"在 ~/.workbuddy/mcp.json 注册 ua mcp 后,Agent 可直接调 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 传 format: md 拿到与 ua info --md 同款的 Markdown 片段"
|
"在 ~/.workbuddy/mcp.json 注册 ua mcp 后,Agent 可直接调 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 传 format: md 拿到与 ua info --md 同款的 Markdown 片段"
|
||||||
],
|
],
|
||||||
"components": []
|
"components": []
|
||||||
|
|||||||
Reference in New Issue
Block a user