fix(a11y): QrCode の aria-live 連呼問題を sr-only debounce 通知で解消 (#435) - #436
Conversation
issue #386 対応で QR プレビュー親 div に role="status" aria-live="polite" を 直貼りしていたが、useEffect が text/errorLevel 変化のたびに SVG を再生成する ため、入力 1 文字ごとに live region が変化と判定され「QRコード: h, QRコード: ht, ...」と SR で連呼される問題があった。 - 視覚プレビュー div から role="status" / aria-live を撤去 (即時表示は維持) - 新たに sr-only な span(role="status" aria-live="polite") を追加し、 svgHtml 変化を 300ms debounce してから「QRコードを生成しました」を announce - 連続入力中は cleanup で timer キャンセル + announcement を空に戻すことで、 同一文言でも入力 stable のたびに毎回 SR 検知される設計 - E2E 陽性対照 2 件追加: - debounce タイミング (200ms 時点で空 / 400ms 時点で本文) - 視覚プレビュー祖先に aria-live が無い構造 (旧実装回帰検知) Closes #435
🖼️ Visual Regression Test 結果
|
レビュー (#436, sha
|
| 観点 | 判定 |
|---|---|
| 設計 (debounce + 空文字経由) | ✅ |
| live region DOM presence | ✅ |
| #434 で達成した accessible name は維持 | ✅ (既存 test を container 起点に書き換え) |
| 陽性対照テストの構造的設計 | ✅ (debounce 機能 / 構造的回帰の 2 軸独立 test) |
| timing E2E の flake 耐性 | 🟡 (マージン拡大推奨) |
| project 規約準拠 | ✅ |
🟡 は本 PR で対応するも独立 follow-up にするも判断可。それ以外は LGTM 🎉
Generated by Claude Code
PR #436 レビュー指摘対応。200ms / 200ms (合計 400ms) では React commit ラグ (50-100ms) で 300ms 境界とほぼ同時となり CI runner で flake になりうる。 100ms / 500ms (合計 600ms) にして境界の前後双方で十分なマージンを確保する。 陽性対照設計 (旧実装に当てれば qr-announcement testid なしで fail) は不変。
|
レビューありがとうございます。🟡 timing flake 耐性のみ対応しました (commit ea64459)。 対応
陽性対照設計 (旧実装には 検証
DEBOUNCE_MS 定数化についてレビュー任意提案の「 Generated by Claude Code |
再レビュー (#436, sha
|
| 観点 | 前回 | 今回 |
|---|---|---|
| 設計 (sr-only debounce 分離) | ✅ | ✅ |
| live region DOM presence | ✅ | ✅ |
| accessible name の維持 (#434 成果) | ✅ | ✅ |
| 陽性対照テスト 2 軸 (debounce / 構造) | ✅ | ✅ |
| debounce E2E の flake 耐性 | 🟡 | ✅ (100ms / 500ms にマージン拡大) |
| project 規約準拠 | ✅ | ✅ |
approve します。
Generated by Claude Code
サマリー
Closes #435 —
QrCodeのaria-liveregion が入力 1 文字ごとに発火して SR で「QRコード: h, QRコード: ht, ...」と連呼される問題を、視覚プレビューと SR 通知を分離する案 B で解消。変更内容
role="status" aria-live="polite"を撤去(即時表示は維持)<span role="status" aria-live="polite" data-testid="qr-announcement">を追加svgHtmlの変化を 300ms debounce してから「QRコードを生成しました」を announceuseEffectcleanup で timer をキャンセル + announcement を空に戻すことで、同一文言でも入力 stable のたびに毎回 SR が変化検知できる設計test-gates 陽性対照
debounce 機能と構造的回帰の 2 軸を、それぞれ独立した
test()に分離して追加:qr-announcementが空、400ms 時点で「QRコードを生成しました」qr-announcementtestid 要素自体が無くtoHaveText段で timeout failqr-code-containerの祖先にaria-liveを持つ要素が無く、[aria-live="polite"]がqr-announcementただ 1 つaria-liveがあるためliveAncestors.toHaveCount(0)で fail検証
node_modules/.bin/astro check→ 0 errors / 0 warnings / 0 hintsnpm run test→ 1247 件 passnpm run test:e2e -- qr-code.spec.ts a11y-live-region.spec.ts→ 17 件 passnpm run format:check→ All files use Prettier code styleTest plan
Closes #435
Generated by Claude Code