[P2] Replay and planner playback push every playback tick through polite live regions, so screen readers keep announcing while replay runs #115
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Priority: P2
Type: bug
Source: full codebase review requested 2026-10-31 (independently reproduced against
2cac7cf)Location:
src/replay/DiveReplayComparison.tsx:335,src/replay/DiveReplayComparison.tsx:57-68,src/replay/DiveReplayComparison.tsx:605-650,src/planner/ProfilePlanner.tsx:196-199,src/planner/ProfilePlanner.tsx:846-916,src/planner/ProfilePlanner.tsx:970-973Problem and evidence
In DiveReplayComparison, the whole
.replay-resultscontainer hasaria-live="polite". Inside it are content that changes with time: the "Replay timeline · m:ss" label, the.summary-overviewdepth and gas-required lines, and ReplayOverlays, which renders fromreplayPointAt(..., time). They sit next to the static table. While playing, asetIntervaladvancestimeevery 50 ms (lines 63-66), so the live region changes at about 20 Hz.ProfilePlanner has the same problem.
.planner-statshasaria-live="polite"and holds depth, ppO2, ceiling, NDL and per-cylinder pressure, remaining gas and GTR for the currentposition.useSimulationClockadvancespositionon every tick. A separate<p role="status">Replay m:ss / total · speed×</p>also changes on every displayed second.The result is that screen readers get a continuous stream of polite announcements for the whole replay. That buries other speech and makes the playback controls hard to use with assistive technology.
I served the HEAD production build (/tmp/divesim-review-dist-SqbiNx) and ran Playwright. I dismissed the welcome dialog with Escape, clicked Planner Mode, then clicked Replay profile with the default plan. I attached a MutationObserver (childList, characterData, subtree) to every
[aria-live]and[role=status]element. Over 5 s it recorded 187 mutations in.planner-statsand 35 in the role=status replay readout, with 70 distinct texts. Examples: "At 0:03 · Depth 3.8 ft · Gas Air · ppO₂ 3.44 psi · GF-high c…", "Replay 0:03 / 21:00 · 8×", "At 0:05 · Depth 5.5 ft …". This matches the claim's 194/33 figures.For the replay comparison I traced the code instead of re-running the browser. Line 335 wraps the timeline label (line 621
Replay timeline · {formatTime(time)}), the summary overview (at(results[name])→ depth and volume) and<ReplayOverlays time={time}>inaria-live="polite". ThesetInterval(..., 50)at lines 63-66 updatestimewhile playing, so every one of these nodes re-renders at 20 Hz. The earlier reviewer measured 521 mutations in 5 s there, which is consistent with this trace.Suggested correction
aria-livefrom.replay-resultsand.planner-stats.role="timer", which implies aria-live=off.aria-valuetext(for example "3:05 of 21:00") so users can get it on demand.Acceptance criteria
Scope: maintenance of the educational simulator. Preserve historical R1 acceptance and operational-validation limits.