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:
@@ -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 / 完成
|
||||
|
||||
Reference in New Issue
Block a user