A modal passed design and QA review, but keyboard users report they can tab out of it into the page behind, and once they do they cannot get back or close it. Diagnose it and tell me what a correct dialog does.
The dialog was built as a styled div, so nothing marks the rest of the page inert. Focus was never moved in, never constrained, never restored on close, and Escape was never wired. A correct dialog moves focus in, keeps it in, restores it, marks background inert and is labelled - which native dialog with showModal gives you almost for free.
What the interviewer is scoring
- Whether the candidate distinguishes a focus trap that is required in a modal from the anti-pattern of trapping a user with no escape
- That focus is moved into the dialog on open and restored to the trigger on close
- Does the answer make background content inert rather than only visually covering it
- Whether Escape and an explicit close control are both provided
- That the candidate reaches for the native dialog element and can say what it does and does not handle
- Whether screen reader behaviour is considered separately from keyboard behaviour, since aria-hidden and inert differ
- Does the answer name how this passed review, and propose a check that would have caught it
Answer
Short answer
It is a div with an overlay, not a dialog. The visual layer covers the page for sighted mouse users and does nothing for anyone else: focus was never moved into it, nothing constrains focus while it is open, nothing restores focus when it closes, and Escape is unbound. A correct modal manages focus on all three edges — in, within, and out — and marks everything behind it inert.
Two different bugs are being reported
Worth separating, because they have different fixes.
Tabbing out into the page behind is a missing focus trap. In a modal dialog, keyboard focus is supposed to cycle within the dialog, because the content behind it is unavailable. If Tab walks into links under the overlay, the user is now interacting with elements they cannot see, which is disorienting in a way that is hard to recover from.
Being unable to get back or close is the more serious failure. WCAG 2.1.2 No Keyboard Trap is failed when a user can move focus somewhere and cannot move it away using the keyboard alone. Note the irony worth stating in an interview: a modal is one of the few places you should constrain focus, and the failure here is not the constraint but the absence of an exit. The distinction is between a focus trap — deliberate, scoped, escapable — and a keyboard trap, which is a conformance failure.
What a correct dialog does
On open, move focus into the dialog. The browser leaves focus on the trigger otherwise, so the first Tab goes to whatever follows the button in the DOM. Focus normally goes to the dialog container itself or to the first meaningful control. Focusing the close button by default is a common choice and often the wrong one — for a destructive confirmation it puts focus on the least useful action and reads out "Close" as the first thing a screen reader user hears about the dialog.
Constrain Tab and Shift+Tab. Cycle from the last focusable element back to the first, and vice versa.
Make the background inert. This is the part that visual overlays fake. The inert attribute removes a subtree from the tab order and from the accessibility tree, so it is invisible to screen reader virtual cursors as well as to Tab. That last point matters: a screen reader user navigating by headings or landmarks can reach the background content even when Tab cannot, so a hand-rolled Tab trap alone does not fix it. aria-hidden="true" hides from assistive tech but leaves elements focusable, which produces the confusing state of focus landing somewhere the screen reader will not announce.
Restore focus on close. Focus returns to the element that opened the dialog. Without this, focus falls back to document.body and the next Tab starts from the top of the page — the user is dumped at the beginning of the document with no idea where they are.
Provide Escape and a visible close control. Escape must close it, and there must be a control that does not depend on knowing that.
Label it. role="dialog" with aria-modal="true", plus aria-labelledby pointing at the heading, so the dialog announces what it is.
Use the platform
Most of this is now native, and reaching for it first is the strongest answer:
<dialog id="confirm" aria-labelledby="confirm-title">
<h2 id="confirm-title">Delete this project?</h2>
<p>This cannot be undone.</p>
<button value="cancel">Cancel</button>
<button value="delete">Delete</button>
</dialog>
const dialog = document.getElementById('confirm');
const opener = document.activeElement;
dialog.showModal(); // moves focus in, traps it, makes the rest inert,
// renders in the top layer, binds Escape
dialog.addEventListener('close', () => {
opener?.focus(); // <-- still yours to do
});
showModal() — not show() — gives you the focus trap, the inert background, the top layer, and Escape handling. What it does not give you is focus restoration on close, sensible initial focus placement (it will focus the first focusable element, which may not be the one you want; autofocus overrides this), or click-outside-to-close. Being able to state that division precisely is what separates knowing the element exists from having shipped it.
If a custom implementation is unavoidable, the WAI-ARIA Authoring Practices dialog pattern is the reference, and a maintained library is a better bet than writing the trap by hand — the edge cases around shadow DOM, iframes, and dynamically added content are where hand-rolled traps break.
Why it passed review
This is worth answering, because the interviewer is usually probing whether you understand the process failure and not just the DOM.
Design review looks at the rendered page and the overlay looks correct. Manual QA uses a mouse, where every behaviour is fine. Automated accessibility checks run against static markup and this is a behavioural defect — axe will happily pass a dialog with no focus management, because the violation only exists while interacting with it.
The checks that would have caught it are cheap. Add a keyboard-only pass to the review checklist for any interactive component: open it, Tab all the way round, press Escape, confirm where focus lands afterwards — thirty seconds. Then encode it as an integration test, since Playwright and Testing Library can assert focus position across a sequence of Tab presses. A test asserting that focus returns to the trigger after close is three lines and would have failed on this exact PR.
© 2026 Preptima. Originally published at preptima.com.
Likely follow-ups
- What is the difference between a focus trap and a keyboard trap, and which one is a WCAG failure?
- Where exactly should focus go when the dialog opens? When would the close button be wrong?
- What does inert give you that aria-hidden does not?
- The native dialog element handles some of this. What is still left for you to implement?
- How would you catch this automatically, given that axe on a static page will not?
Related questions
- Design rejected the native control, so you are building a custom one. How do you make it accessible?hardAlso on accessibility and aria5 min
- This form marks invalid fields with red text and a red border. What has to change?mediumAlso on accessibility and wcag4 min
- A toast pops up saying the changes were saved, and a screen reader user hears nothing. Why, and what do you change?mediumAlso on accessibility and aria5 min
- How do you implement a scalable architecture and tooling to automatically enforce strict WCAG 2.1 AA compliance across dozens of autonomous frontend teams without crippling developer velocity?hardAlso on accessibility and wcag3 min