docs: P9-B UX batch-2 bookkeeping — ROADMAP row + doc index + CHANGELOG 0.3.3 (crearte 0.24.0, merged 338acbf)

This commit is contained in:
2026-10-01 23:15:05 +08:00
parent 015c2b9696
commit 0e63dc2448
2 changed files with 9 additions and 0 deletions
+7
View File
@@ -6,6 +6,13 @@ All notable changes to this repository should be documented in this file.
The format loosely follows Keep a Changelog and can be adapted to the team's habits.
本文档参考了 Keep a Changelog 的思路,也可以根据团队习惯调整。
## [0.3.3] - 2026-10-01
### Done / 完成
- P9-B UX batch-2 (accessibility & keyboard-efficiency pack) delivered: crearte `338acbf` (0.24.0, merge-base `ee4edf7`) — five tasks closing seven measured a11y gaps, T1-T4 dispatched to subagents and T5 written by the controller, each through implement → task-level review → adjudication (four reviews, all PASS), plus a whole-branch final review on the strongest available model cross-checking spec §1-§6 in full, the cross-task integration seams, and mutation-testing three guards. **Contrast to WCAG AA**: `--color-success` deepened `#2fa46a` → `#1f7a4d` (paper-on 2.83 → 4.76; the success toast is 12px bold, not large text) and the error toast switched `bg-accent` → `bg-accent-ink` (3.64 → 4.87, reusing an existing token rather than adding a colour). **Toast announcer restructured** (closing P10 final-review item T1(d), an interactive control living inside a live region): visual and announcement layers split the way Radix Toast / Headless UI do it — one persistent `sr-only p[role=status][aria-live=polite]` whose text a watch drives, no announcement semantics on the visible toasts, so the dismiss button is a sibling of the live region rather than a descendant. **Skip link** as the root div's first child before `<AppHeader />`, `sr-only` until focused then `focus:not-sr-only` with `focus:z-[80]` above the toast layer. **Focus management after SPA navigation**: `<main id="main" tabindex="-1">` plus an exported `focusMain()` called from the router's `afterEach` only when `to.path !== from.path` — query-only changes (P10's clickable tags, `/games?tag=x`) deliberately do not steal focus, `preventScroll` avoids double-scrolling against `scrollBehavior`, and a missing `#main` no-ops via the optional chain. **Table column headers**: all 24 `<th>` elements across the three admin views get `scope="col"`, and the two previously empty 操作 headers gain `<span class="sr-only">操作</span>` (they were unnamed columns to a screen reader). **Rating accessible names**: each star button gets `aria-label="评 N 星"` with its glyph `aria-hidden`, and the existing group span becomes `role="group"` with a dynamic label — deliberately not `radiogroup`/`aria-checked` (the control supports click-current-star-to-unrate, which radio cannot express) and not per-star `aria-pressed` (lighting is a continuous run, so pressed would announce four stars as all-pressed). **Two source-level guards** following the existing `no-gradient.test.ts` pattern: `lib/contrast.test.ts` parses `@theme` tokens out of `main.css` and asserts WCAG 2.1 ratios for eleven pairs (ten ≥4.5 plus the focus ring ≥3 per 1.4.11), `lib/tableScope.test.ts` scans every `app/**/*.vue` asserting each `<th` opening tag carries `scope=` (word-boundary match so the five `<thead>` occurrences are not flagged; opening-tag slice taken across newlines). **New e2e** `a11y.spec.ts`: real keyboard Tab path, admin `columnheader` names, star accessible names via `toHaveAccessibleName`. Zero server/deploy changes. Acceptance (five legs, green on merge tip, independently re-run six-for-six by the final reviewer): vitest **626** (baseline 601, +25) / vue-tsc clean / build + build-runtime OK / main e2e **80+1skip** (baseline 77+1skip) / noauth 4. Final verdict APPROVE with notes — zero critical, zero important. Two spec-layer corrections landed mid-wave: ① the T1 implementer caught a real defect in the spec's initial D-I design (a `role=status` mounted *together with* its text is not reliably announced by several screen-reader/browser combinations, trading button noise for total silence) → re-pinned to D-I′, fixed by the controller in `f01baae`, which also pinned the saturation trap (the watch must key on the last entry's **id**, not the array length — at `MAX_VISIBLE=3` a push evicts and appends in one assignment so the length stays 3 while a fourth message still has to be announced). ② Two quantity slips in the controller's own briefs (contrast 3.16 vs the real 2.83 for paper; "19 `<th>`" was a line count, the element count is 24) were both corrected by implementers deferring to the spec and to measurement; spec §1 was amended to match. Lesson recorded: quantities quoted in dispatch briefs must be verified at element level, never inferred from `grep -c` or from an adjacent number in the spec. Four items parked in the polish batch (re-check a weak router-test assertion on vue-router upgrade; surface unrate semantics in the accessible name; the announcer re-reads an older message when the newest toast is manually dismissed; spec wording already fixed). C (dark mode), OG social cards, play count, mobile header and later batches remain parked pending owner go-ahead.
- P9-B UX 第二批(可访问与键盘效率包)交付:crearte `338acbf`(0.24.0,merge-base `ee4edf7`)——五任务闭合七处实测可访问缺口,T1-T4 派发子代理、T5 由控制者本人写,每任务经实现→任务级审查→裁定(四次审查全 PASS),另加最强可用模型的全分支终审,对 spec §1-§6 全量对照、跨任务集成缝隙、并对三处守卫做 mutation 式抽查。**色彩对比达 WCAG AA**:`--color-success` 由 `#2fa46a` 加深为 `#1f7a4d`(paper-on 2.83 → 4.76;成功 toast 是 12px 粗体,不属大字号),错误 toast 由 `bg-accent` 改为 `bg-accent-ink`(3.64 → 4.87,复用既有令牌而非新增颜色)。**toast 播报区重构**(收口 P10 终审转办项 T1(d):交互控件位于实时区内):按 Radix Toast / Headless UI 的做法把视觉层与播报层解耦——一个常驻的 `sr-only p[role=status][aria-live=polite]` 由 watch 驱动其文字,可见 toast 不带任何播报语义,于是关闭按钮成为实时区的兄弟而非后代。**跳转链接**作根 div 首子、在 `<AppHeader />` 之前,未聚焦时 `sr-only`、聚焦后 `focus:not-sr-only` 且 `focus:z-[80]` 压过 toast 层。**SPA 导航后焦点管理**:`<main id="main" tabindex="-1">` + 导出的 `focusMain()`,仅当 `to.path !== from.path` 时由路由 `afterEach` 调用——只改 query(P10 的可点标签 `/games?tag=x`)有意不抢焦点,`preventScroll` 避免与 `scrollBehavior` 双重滚动,`#main` 缺失时由可选链静默 no-op。**表格列头**:三个管理视图全部 24 个 `<th>` 元素加 `scope="col"`,两个此前为空的「操作」表头补上 `<span class="sr-only">操作</span>`(它们在读屏里曾是无名列)。**评分可访问名**:每颗星 `aria-label="评 N 星"` 且字形 `aria-hidden`,既有分组 span 成为 `role="group"` 并带动态标签——有意不用 `radiogroup`/`aria-checked`(本控件支持点当前分撤评,radio 无法表达取消选中),也不用逐星 `aria-pressed`(点亮是连续区间,pressed 会把四分播报成四颗星都按下)。**两个源码级守卫**沿用既有 `no-gradient.test.ts` 范式:`lib/contrast.test.ts` 从 `main.css` 解析 `@theme` 令牌并对十一个配对断言 WCAG 2.1 比值(十个 ≥4.5 加焦点环 ≥3,依 1.4.11),`lib/tableScope.test.ts` 扫描全部 `app/**/*.vue` 断言每个 `<th` 起始标签都带 `scope=`(词界匹配故五处 `<thead>` 不被误判;起始标签片段跨换行截取)。**新增 e2e** `a11y.spec.ts`:真实键盘 Tab 路径、管理端 `columnheader` 可访问名、经 `toHaveAccessibleName` 验证的星标可访问名。零后端/部署改动。验收(五腿,合并 tip 全绿,终审者六腿独立实跑逐条一致):vitest **626**(基线 601,+25)/ vue-tsc 零错 / build+build-runtime OK / 主 e2e **80+1skip**(基线 77+1skip)/ noauth 4。终审裁定 APPROVE with notes——零关键、零重要。波次内落两处 spec 层修正:① T1 实现者抓出 spec 初稿 D-I 的真缺陷(与文字**同时创建**的 `role=status` 在多个读屏+浏览器组合下不被可靠播报,等于用按钮噪音换来彻底静默)→ re-pin 为 D-I′,控制者于 `f01baae` 直修,并顺带钉死饱和态陷阱(watch 必须以末尾条目的 **id** 而非数组长度为源——`MAX_VISIBLE=3` 时 push 会「逐最旧+append」一次赋值完成,长度停在 3 而第四条消息仍需播报)。② 控制者自己简报里的两处数量笔误(对比度 3.16 与 paper 的真实值 2.83、「19 个 `<th>`」是行计数而元素实为 24)均由实现者以 spec 与实测为准纠正,spec §1 已同步修正。教训已入账:派发简报里引用的数量必须元素级核实,不得由 `grep -c` 或 spec 中相邻数字推断。四条转入打磨批(vue-router 升级时复看一处弱断言、撤评语义进可访问名、手动关掉最新 toast 时播报区会重念一条旧消息、spec 口径已修)。C(暗色模式)、OG 社交卡片、播放计数、移动端页头及后续批仍挂账待 owner 启动。
## [0.3.2] - 2026-10-01
### Done / 完成
+2
View File
@@ -36,6 +36,7 @@
| P8 | 长尾打包 | 权限开关 UI 全量(`inlineStyle/wasm/coop/fullscreen/gamepad`)、后端 triage 小项(`games.Detail` 400 细分、admin 路由 slug 校验、approve 同名竞态测试)、账号注销、静态兜底目录、CHANGELOG 模板文案 | **第一批完成**(②⑤①,2026-10-01 合并推送:server `921443a` 0.14.0 / crearte `70ba10c` 0.20.0。容器四连+空库 `-count=1` 集成全绿(新竞态测试含,skip 守卫 0)+`-race` 竞态腿 PASS;curl 冒烟全中:detail 400 细分 user:/slug: 且双非法 user 优先、admin 四路非法 400 `invalid work id`、合法格式不存在仍 404;FE vitest 499/typecheck/admin-flow 7/主套件 70+1skip;双审 PASS(server PASS with notes—唯一 note 双非法优先级缺钉桩,已以 `ee5b960` 测试断言补上);deploy 零改动。**第二批完成(③账号注销+④静态兜底目录)**(2026-10-01 合并推送:server `12b8ffb` 0.15.0(迁移 0011 墓碑化删行、DELETE /api/auth/account 对密码注销、唯一 admin 409、user delete CLI、catalog export 出 schemaVersion=2 目录)+ crearte `7d5f338` 0.21.0(/account 危险区+deleteAccount 客户端+AdminUsersView 三码中文化钉桩)+。真库注销链集成+分支真产物 curl 冒烟 14 项全 PASS(墓碑行/邮箱复用/409/旧 token 作废/CLI delete/export schema);FE vitest 512/typecheck/主套件 71+1skip/noauth 4,双审 PASS with notes(前端 2 条次要已钉桩 `7a54152`,server 裁决项:LOG_LEVEL 大小写不一致—deferred)。余 ④的 render 消费端接入按需(feed 已交付)**,CHANGELOG 头部去模板腔两仓已随第一批完成、wrapper 头部不在 spec 范围 |
| P9 | UX 增强(第一批:浏览器反馈包) | 路由级 `document.title` 两段式(router `afterEach` 按 19 个 name 全表设静态基线 + 四个数据页 watch 数据后精化)+ 作品页 `meta description`(截 120 码点、默认回落)+ `StatePanel` 骨架对齐(cards 3→6 张、新增 `lines` 变体六页八实例)。零后端/部署改动 | **第一批完成**(2026-10-01 合并推送:crearte `26cd625` 0.22.0(`a196603`+`3f1de38`+审查补钉 `e9e4c56`)。五腿全绿:vitest **551**(基线 512+新增 39)/ vue-tsc 零错 / build OK / 主套件 72+1skip(含新增标题 e2e 腿,`landing.spec.ts` 首条兼容钉一字未动)/ noauth 4。独立审查 PASS with notes:ISSUE-1(离开作品页 description 未回落默认,spec D-C 子项)与 ISSUE-2(「七页」实为六页文案)均已在合主前补钉+补测。B 效率可访问 / C 暗色模式两批挂账待启动) |
| P10 | UX 增强(第一批:日常交互包) | toast 体系(useToast 单例 store + ToastHost 挂 App.vue)+ 投稿表单脏数据离开守卫(onBeforeRouteLeave confirm + beforeunload)+ 作品标签可点(GameView/GameCard tags → /games?tag= RouterLink)+ 作品页复制链接分享(clipboard + toast)+ 最近玩过条带(recent.ts localStorage + GameHost ready 记录 + LandingView 条带)+ 页头下拉外点/Esc 关闭。零后端/部署改动 | **第一批完成**(2026-10-01 合并推送:crearte `ee4edf7` 0.23.0,merge-base `26cd625`。六任务各经实现→任务级审查→裁定,另加全分支终审(spec §3/§4/§5 全量对照)。五腿全绿:vitest **601**(基线 551+新增 50)/ vue-tsc 零错 / build OK / 主套件 **77+1skip**(基线 72+1skip)/ noauth 4。审查补钉四处:`fc0270e`(AppHeader 卸载清理 spy)、`dbf4bf0`(hosted 重开 sync-flush 再记,spec D-H re-pin)、`bb9bb35`(e2e 夹具修正——spec 测试计划自身误选 external-runtime 夹具 2048/a-dark-room,换 abs-paths/storage)、`836b2fb`(AppHeader 路由收起回归,spec §5 明列项)。终审裁定「需修复后合并」两条件均已闭合。B 效率可访问 / C 暗色模式 / OG 卡片 / 播放计数等后续批挂账待启动) |
| P9-B | UX 增强(第二批:可访问与键盘效率包) | WCAG AA 色彩对比达标(`--color-success` 加深 + error toast 换 `bg-accent-ink`)+ toast 播报区重构(常驻 sr-only 播报层与视觉层解耦,收口 P10 终审转办项 T1(d)「实时区嵌交互按钮」)+ skip-link(`App.vue` 根首子,`focus:z-[80]`)+ SPA 导航后焦点管理(`focusMain()`,仅路径变化触发、`preventScroll`)+ 管理端表格 24 个 `<th>` 补 `scope="col"`(两处空表头补 sr-only「操作」)+ 五星评分可访问名(逐星「评 N 星」+ 分组 `role="group"` 动态标签 + 字形 `aria-hidden`)。另加两个源码级守卫(`contrast.test.ts` 十一配对对比度、`tableScope.test.ts` 全仓 `<th` 必带 scope)与 `e2e/a11y.spec.ts`。零后端/部署改动 | **第二批完成**(2026-10-01 合并推送:crearte `338acbf` 0.24.0,merge-base `ee4edf7`。五任务(T1-T4 派发 + T5 控制者本人)各经实现→任务级审查→裁定,四次任务级审查全 PASS,另加全分支终审(spec §1-§6 全量对照 + 跨任务集成缝隙 + mutation 抽查)。五腿全绿:vitest **626**(基线 601+新增 25)/ vue-tsc 零错 / build+build-runtime OK / 主套件 **80+1skip**(基线 77+1skip)/ noauth 4。终审独立实跑六腿与控制者数字逐条一致,裁定 APPROVE with notes(零关键/零重要)。波次内两次 spec 层修正:① T1 实现者抓出 spec 初稿 D-I 缺陷(per-toast `role=status` 与文字同时创建,部分读屏不播报首条)→ re-pin D-I′ 为常驻播报区,控制者直修 `f01baae` 并附带钉死饱和态 watch 陷阱(源必须是末尾 id 而非长度,`MAX_VISIBLE=3` 时 3→3 不触发);② 控制者简报两处数量笔误(对比度 3.16 应为 2.83、「19 个 th」是行计数、元素实为 24)均由实现者以 spec/实测为准纠正,spec §1 已同步。打磨批挂账四条(router 升级复看弱断言、撤评语义进可访问名、手动关最新 toast 的播报重念、spec 口径已修)。C 暗色模式 / OG 卡片 / 播放计数 / 移动端页头等后续批挂账待启动) |
## 排序理由
@@ -64,3 +65,4 @@
| P8 长尾打包(第二批:③④) | `docs/specs/2026-10-01-p8b2-deletion-catalog-design.md` | `docs/plans/2026-10-01-p8b2-account-catalog.md`(已执行,2026-10-01:server 0.15.0 / crearte 0.21.0) |
| P9 UX 第一批(浏览器反馈包) | `docs/specs/2026-10-01-p9-ux-browser-feedback-design.md` | `docs/plans/2026-10-01-p9-ux-browser-feedback.md`(第一批已执行,2026-10-01:crearte 0.22.0) |
| P10 UX 第一批(日常交互包) | `docs/specs/2026-10-01-p10-ux-daily-interaction-design.md` | `docs/plans/2026-10-01-p10-ux-daily-interaction.md`(第一批已执行,2026-10-01:crearte 0.23.0) |
| P9-B UX 第二批(可访问与键盘效率包) | `docs/specs/2026-10-01-p9b-a11y-keyboard-design.md` | `docs/plans/2026-10-01-p9b-a11y-keyboard.md`(第二批已执行,2026-10-01:crearte 0.24.0) |