Accessibility Session Replay: Spotting Keyboard and Zoom Traps

Session replay catches accessibility failures that DOM scanners never will, because these barriers only exist as behavior. A keyboard tabbing in circles. A page scrolled sideways to read one line. A form that dies to a timeout before anyone hits submit. Automated tools catch roughly 57% of WCAG issues (Rocket Validator puts axe-core at "approximately 57% of the WCAG issues typically found during a first-time accessibility audit"), which leaves the rest to a human watching focus order and logical flow. Accessibility session replay is how you watch that human-judgment half happen. But heavy privacy masking can wireframe out the very interactions you're trying to diagnose, and that tension runs through everything below.

The privacy failure mode comes first

Before the technique, the trap. Replay only helps with accessibility if you're actually recording the accessibility-relevant interactions, and default masking often erases them. Fullstory documents its own behavior plainly: "For masked elements containing text, all text will be replaced by irreversibly transformed placeholder text meant to resemble a wireframe of the original content." In plain speech: the masked field keeps its size, color, and character count, but the words become noise you can't read back.

That's usually the right call for content. The problem is when masking also flattens the interaction: where focus landed, whether the cursor entered the field, how long someone sat there re-typing. Lose that and you've thrown out the diagnostic signal along with the PII.

Here's where the creepy line comes in, and it's a line I keep coming back to. Recording an assistive-technology user's struggle to escape a modal, without their consent, specifically so you can study it โ€” that's the wrong side of it. You're not entitled to watch a disabled person fight your interface just because your analytics tool can. Consent to record comes first. Masking decisions come second. Everything below assumes you've already cleared the first bar, because no amount of clever timeline reading justifies watching someone who never agreed to be watched.

Where scanners hit a ceiling

Deque's own coverage study, drawing on over 2,000 audits and roughly 300,000 issues, "found that, on average, 57 percent of accessibility issues were completely covered by this automated testing." That number is genuinely useful. 57% is a lot of low-hanging color-contrast and missing-alt-text problems handled without a human. But it also means about 43% needs someone to make a judgment call.

Focus order isn't a rule a crawler can fully evaluate. It's a sequence you have to watch to know whether it makes sense. The same goes for logical flow, reading order, and the question of whether a keyboard user can actually finish a task. Replay is the layer that catches what the scanner shrugs at: not a replacement for axe-core, a partner to it, working the half the scanner can't reach.

The checklist: replay-visible failures mapped to WCAG

Four barriers show up as distinct signatures on a replay timeline. Here's the compact version, then each one expanded so you know what you're actually looking at when you scrub through a session.

Barrier What it looks like on the timeline WCAG criterion Repro steps
Keyboard trap Rapid repeated Tab events cycling the same elements; focus never leaves a widget SC 2.1.2 (Level A) Tab into the component, try every standard key to exit
Focus loss / invisible focus Keyboard nav with no visible indicator moving; hesitation, re-tabbing SC 2.4.7 (Level AA) Tab through the page, watch for a visible focus ring
Pinch-zoom / reflow fight Sustained horizontal scroll, pinch gestures, sideways drag to read one line SC 1.4.10 (Level AA) Load at 320 CSS px width, check for two-axis scrolling
Timeout destroys work Long form entry, sudden reset/redirect, re-typing or rage-clicks SC 2.2.1 (A) & SC 2.2.6 Fill a form, wait past the session limit, attempt submit

Keyboard traps (SC 2.1.2, Level A)

The W3C states it precisely: "If keyboard focus can be moved to a component of the page using a keyboard interface, then focus can be moved away from that component using only a keyboard interface, and, if it requires more than unmodified arrow or tab keys or other standard exit methods, the user is advised of the method for moving focus away." Translated: if you can Tab into it, you must be able to Tab out of it. And if Escape or arrows are the trick, you have to tell people.

On the timeline this reads as a tight loop. Tab, Tab, Tab, Tab, all landing on the same three or four elements, no navigation event ever leaving the widget's DOM subtree. Date pickers and custom dropdowns are repeat offenders. To reproduce: keyboard-focus into the component, then try Tab, Shift+Tab, Escape, and arrow keys in turn. If none of them free you, you've confirmed the trap.

Focus loss and invisible focus (SC 2.4.7, Level AA)

This is the one everybody fails. TestParty, summarizing WebAIM's 2024 analysis of one million home pages, notes that "78% had detectable focus indicator issues," making Focus Visible "one of the most frequently failed WCAG criteria." Design systems that strip the browser's default outline "for aesthetics" and never replace it are the usual culprit.

The replay signature is subtle. Keyboard navigation happening, DOM focus moving, but nothing visibly changing on screen. You'll often see it paired with human hesitation, a pause, a re-tab, a second attempt, because the person literally can't tell where they are. Reproduce by Tabbing through the page with your hands off the mouse and asking, at each stop, can I see where focus is? If the answer is no even once inside an interactive path, log it.

Pinch-zoom and reflow fights (SC 1.4.10, Level AA)

Reflow requires content to stay usable without two-dimensional scrolling at a narrow width. Includia states the equivalence cleanly: "320 CSS pixels is equivalent to a starting viewport width of 1280 CSS pixels at 400% zoom (1280รท4=320)." So the person who zooms a desktop page to 400% and the person on a small phone are hitting the same requirement.

When it breaks, the timeline fills with horizontal scroll events. Sideways drag, then drag back, then sideways again to catch the end of a sentence that runs off the viewport. Pinch gestures on mobile replays tell the same story. To reproduce, set your viewport to 320 CSS pixels wide and read the page top to bottom. If you're scrolling left-right to consume normal text, reflow is failing.

Timeout expiries that destroy work (SC 2.2.1, A, and SC 2.2.6)

Two criteria stack here. SC 2.2.1 Timing Adjustable, per Dock's WCAG documentation, means "if a time limit is set, users must be able to turn it off, adjust it, or extend it, unless the time limit is genuinely essential." SC 2.2.6 Timeouts, in Deque University's phrasing, requires that "users are warned of the duration of any user inactivity that could cause data loss, unless the data is preserved for more than 20 hours." Plain speech: don't silently blow away someone's half-finished form, and if you preserve their work for 20+ hours you're off the hook for the warning.

The behavioral tell is grim to watch. A long stretch of careful form entry, then a page reset or a redirect to a login screen, then the same fields getting re-typed, or a burst of rage-clicks on a submit button that no longer works. Reproduce by filling a form partway, idling past the session limit, and trying to submit. If your work vanishes without warning, both criteria are in play.

Reading one replay session for a11y friction

Let me narrate a session the way I'd write it in my notes. A checkout replay, four minutes, keyboard user, desktop.

The person lands on the shipping form and starts tabbing. First red flag at 0:41 โ€” they reach the date-of-birth picker and get stuck. I count 37 Tab presses over the next 90 seconds, 0 successful escapes from that widget. Focus is clearly inside it (the DOM tells me), but there's no visible ring, so they're also fighting SC 2.4.7 at the same time. Two of my checklist rows firing in one component.

Around 2:10 they zoom the page, probably to read the tiny helper text under the card field. Now the timeline lights up with horizontal scroll: 11 horizontal scroll reversals in about forty seconds, left-right-left, the reflow-fight signature straight out of the SC 1.4.10 row. Then at 2:58 the session dies mid-checkout. A redirect to the sign-in page, the form emptied, no warning captured anywhere in the events. SC 2.2.1 and SC 2.2.6, textbook.

One session, three distinct barriers, each mapping to a specific criterion I can hand to a developer. The counts turn a vague "the checkout feels rough" into 37 traps, 11 reversals, one silent data loss.

The masking dilemma, revisited

So back to the creepy line, now as a decision framework. The rule I use: mask the content, preserve the interaction structure. The credit-card number, the date of birth, the email address, wireframe them, transform them, keep the words unreadable. But keep the focus states, the scroll behavior, and the event timing intact, because those are the diagnostic layer and none of them are PII on their own.

If your masking config is so aggressive that you can't tell where keyboard focus landed or how many times someone scrolled sideways, you've protected privacy and destroyed the accessibility signal in the same stroke. There's a middle setting, and finding it is the actual work. Our own guide to masking PII without losing the insight walks through the trade-offs, and if you're unsure whether you can legally record at all, the wiretapping-lawsuit overview is the place to start.

A repeatable review pass

Here's the pass you can run tomorrow.

  1. Check your masking config first. Confirm it hides content but not focus states, scroll events, or event timing. If focus and scroll are gone, fix this before watching anything.
  2. Filter for frustration near forms. Rage-clicks and dead-clicks clustered around inputs are your fastest route to the worst sessions. Our rage-clicks explainer covers the detection thresholds.
  3. Watch for the four signatures. Tab loops, invisible focus, horizontal-scroll reversals, silent form resets. You now know each one on sight.
  4. Log every hit with its SC number and repro steps. "Keyboard trap in date picker, SC 2.1.2, Tab in and no key exits" beats a screen recording nobody can search.
  5. Hand developers the repro, not the video. They can fix a reproducible step. They can't triage a four-minute clip.

Most replay tooling, including rrweb-based web capture, already records keyboard and scroll and timing events, so the raw material is usually there. The failure is almost always in how it's masked, not whether it's captured.

Takeaway

Scanners tell you what rule broke. Replay tells you who it broke for and how badly: the 37 traps, the 11 reversals, the form that died at 2:58. That's the 43% axe-core hands back to you, and it's the half where real people are actually stuck. Just remember which side of the creepy line you're on. Consent first, mask the content, keep the interaction. Watch respectfully, or don't watch.