实施 #3406(字数统计 aria-label 键化)时按 PM 裁定只做键化、不改行为,把 #3406 正文提出的「aria-live + 每击键重算是否本身该重设计」拆出来单独立单,并补上实测。
实测(在 #3406 的分支上跑,行为与 origin/main 一致)
在 zh 会话下渲染 TextAreaField(maxLength: 500),逐字输入一句 52 字符的普通备注(Follow up with the customer about the renewal quote.),记录每次 change 之后 live region 的可访问名:
{
"keystrokes": 52,
"ariaLiveOnRegion": "polite",
"regionIsAriaHidden": null,
"textareaDescribedBy": null,
"announcementsFired": 52,
"distinctAnnouncements": 52,
"charsSpokenPerKeystroke": 18,
"totalCharsSpoken": 979,
"first": "已输入 1 个字符,最多 500 个",
"last": "已输入 52 个字符,最多 500 个"
}
即:52 次击键 = 52 次互不相同的整句播报,累计 979 个字符,没有任何 debounce、没有阈值门控。用户想输入的那句话本身只有 52 个字符 —— 播报量是输入量的近 19 倍,且每一次都会打断读屏对用户自己输入内容的回读。
另外两点顺带测到的、与之相关的结构问题:
regionIsAriaHidden: null —— 可见的 {n}/{max} 数字没有 aria-hidden,它既是视觉计数器又是 live region 本体;
textareaDescribedBy: null —— textarea 与这个计数器之间没有 aria-describedby 关联,所以聚焦字段时读屏不会提到「有字数上限」,只有开始打字之后才被动地一遍遍听见。
可达性
只在字段元数据声明了 maxLength 时渲染,且只有读屏用户感知 —— 与 #3406 同一块 DOM、同一批用户。不是死代码,当前每一个带 maxLength 的长文本字段都命中。
候选模式(未裁定,列出来供 triage)
- A. debounce 后再播报:可见数字标
aria-hidden="true",另起一个 visually-hidden 的 aria-live="polite" 状态节点,输入停顿约 1s 后才更新。这是 GOV.UK Design System 的 character-count 组件采用的形状,也是改动面最小、语义最稳的一种。
- B. 阈值门控:剩余字数进入末段(如剩余 10% 或最后 20 字)之前 live region 保持静默 —— 计数在快到上限时才真正有信息量。可与 A 叠加。
- C. 取消 live region,改成
aria-describedby:计数器挂到 textarea 的 aria-describedby 上,聚焦时读一次、之后按需查询,完全不打断输入。信息可得性最好,但失去「接近上限」的主动提示,故通常要配 B。
- D. 维持现状:显式列出以便被否掉,而不是默认继承。
个人倾向 A + B(先静默、临近上限再以 debounce 后的整句提示),但这是可访问性契约形状问题,不自行裁定。
不是 #3406 的子问题:PM 已明确把 #3406 的完成范围裁定为「键化,行为不变」,本条落在那条边界之外,故独立立单。也不构成阻塞 —— 本条可以在 #3406 落地前后任一时点做,只是两者会碰同一段 JSX(#3406 的 PR 里已把该 aria-live 属性用测试钉住,做本条时需要一并更新那条 pin)。新键 fields.textarea.characterCount 由 #3406 引入;若采用 B,可能还需要一条「接近上限」的新文案键,同样要十包齐。
未打标签,交 PM triage 定级 —— 我判断它是「当前有人踩得到的具体缺陷」而非观察类,但立单时的严重度判断本就不可靠,不自行加权。
Generated by Claude Code
实施 #3406(字数统计 aria-label 键化)时按 PM 裁定只做键化、不改行为,把 #3406 正文提出的「
aria-live+ 每击键重算是否本身该重设计」拆出来单独立单,并补上实测。实测(在 #3406 的分支上跑,行为与
origin/main一致)在
zh会话下渲染TextAreaField(maxLength: 500),逐字输入一句 52 字符的普通备注(Follow up with the customer about the renewal quote.),记录每次 change 之后 live region 的可访问名:{ "keystrokes": 52, "ariaLiveOnRegion": "polite", "regionIsAriaHidden": null, "textareaDescribedBy": null, "announcementsFired": 52, "distinctAnnouncements": 52, "charsSpokenPerKeystroke": 18, "totalCharsSpoken": 979, "first": "已输入 1 个字符,最多 500 个", "last": "已输入 52 个字符,最多 500 个" }即:52 次击键 = 52 次互不相同的整句播报,累计 979 个字符,没有任何 debounce、没有阈值门控。用户想输入的那句话本身只有 52 个字符 —— 播报量是输入量的近 19 倍,且每一次都会打断读屏对用户自己输入内容的回读。
另外两点顺带测到的、与之相关的结构问题:
regionIsAriaHidden: null—— 可见的{n}/{max}数字没有aria-hidden,它既是视觉计数器又是 live region 本体;textareaDescribedBy: null—— textarea 与这个计数器之间没有aria-describedby关联,所以聚焦字段时读屏不会提到「有字数上限」,只有开始打字之后才被动地一遍遍听见。可达性
只在字段元数据声明了
maxLength时渲染,且只有读屏用户感知 —— 与 #3406 同一块 DOM、同一批用户。不是死代码,当前每一个带maxLength的长文本字段都命中。候选模式(未裁定,列出来供 triage)
aria-hidden="true",另起一个 visually-hidden 的aria-live="polite"状态节点,输入停顿约 1s 后才更新。这是 GOV.UK Design System 的 character-count 组件采用的形状,也是改动面最小、语义最稳的一种。aria-describedby:计数器挂到 textarea 的aria-describedby上,聚焦时读一次、之后按需查询,完全不打断输入。信息可得性最好,但失去「接近上限」的主动提示,故通常要配 B。个人倾向 A + B(先静默、临近上限再以 debounce 后的整句提示),但这是可访问性契约形状问题,不自行裁定。
与 #3406 的关系
不是 #3406 的子问题:PM 已明确把 #3406 的完成范围裁定为「键化,行为不变」,本条落在那条边界之外,故独立立单。也不构成阻塞 —— 本条可以在 #3406 落地前后任一时点做,只是两者会碰同一段 JSX(#3406 的 PR 里已把该
aria-live属性用测试钉住,做本条时需要一并更新那条 pin)。新键fields.textarea.characterCount由 #3406 引入;若采用 B,可能还需要一条「接近上限」的新文案键,同样要十包齐。未打标签,交 PM triage 定级 —— 我判断它是「当前有人踩得到的具体缺陷」而非观察类,但立单时的严重度判断本就不可靠,不自行加权。
Generated by Claude Code