Modal Open, Focus Gone: How to Audit Dialogs with a Keyboard
A centred overlay is not automatically a working modal dialog. Audit focus, keyboard navigation, Escape, inert backgrounds, focus return, and native dialog semantics.
A modal opens. Visually, everything is obvious: the background dims, a form appears in the centre, and an X sits in the top-right corner.
Then somebody presses Tab.
Focus moves behind the modal. Or becomes invisible. Or lands immediately on “Delete” even though the action is irreversible. When the dialog closes, the user suddenly ends up at the top of the page. With a mouse, the interface still looks completely polished.
A modal is not accessible because it looks like a modal. It has to behave like one.
That makes dialogs a surprisingly effective frontend quality test. A small UI component combines semantics, focus management, keyboard operation, scrolling, overlays, portals, and state transitions.
What a modal dialog actually promises
The W3C ARIA Authoring Practices Guide describes a modal dialog as a window where the content underneath is inert: while the dialog is active, users cannot interact with content outside it. Its tab sequence is also contained. Tab and Shift+Tab should not move focus into the page behind the dialog.
Several expectations need to hold at the same time:
- opening the dialog moves focus into it in a meaningful way,
- keyboard navigation remains inside the active dialog,
Escapecan close the dialog,- there is a visible close or cancel action,
- background content is genuinely unavailable for interaction,
- closing returns focus to a logical place,
- screen readers receive a useful name and context for the dialog.
A half-implemented modal can therefore be worse than an ordinary page section: visually it claims to take over the interaction, while technical navigation continues across two layers at once.
Red flag 1: The background is darker but still active
A backdrop is styling. Modality is behaviour.
If links, forms, or buttons behind the overlay remain reachable by keyboard, the background is not actually inert. Making it difficult to see with a mouse does not fix that.
The APG is explicit: content outside an active modal dialog should not be interactive. With a native HTML dialog opened through showModal(), the browser handles an important part of this model and blocks or makes the rest of the document inert.
A custom div overlay has to recreate that behaviour correctly and keep doing so through refactors, portal changes, and nested overlays. That is where many regressions begin.
Audit question: while the modal is open, can Tab, Shift+Tab, a screen reader, or a click still reach something behind it?
If yes, it is visually modal but technically not.
Red flag 2: Focus lands somewhere — good enough
“Focus moved” is not the same as good focus management.
The current HTML Living Standard explicitly notes that appropriate initial focus depends on the dialog's content. In a short input form, the first meaningful field may be right. In a longer dialog containing several paragraphs or a table, a static starting point near the beginning can make more sense so users encounter the structure in a useful order.
The W3C APG describes several cases:
- Structured, longer content may benefit from a static element at the beginning with
tabindex="-1"receiving initial focus. - For an irreversible operation, the least destructive action may be the better initial focus target.
- For a simple continuation dialog, the most likely action may be appropriate.
- Focusing a control far down in a large dialog can scroll the beginning of the content out of view.
The rule is therefore not “always focus the first input” or “always focus the close button.”
A better rule is: what does the person need to understand or do next?
Red flag 3: aria-modal="true" is expected to do all the work
ARIA describes meaning. It does not repair behaviour.
A div role="dialog" aria-modal="true" does not automatically become a functioning modal. The APG even warns against marking a dialog as modal when it does not actually behave modally for other users.
A custom dialog still needs:
- appropriate dialog semantics,
- a useful accessible name, usually through
aria-labelledbyreferencing a visible heading or througharia-label, - correct focus management,
- genuine background inertness,
- keyboard operation,
- a reliable way to close.
Do not attach aria-describedby to the entire dialog by habit either. For complex content with lists, tables, or multiple paragraphs, the APG advises against flattening the whole structure into one long screen-reader description.
More ARIA does not automatically create more accessibility. Native HTML is often the stronger starting point.
Prefer native dialog over a custom focus machine
W3C Technique H102 describes the HTML dialog element as a robust way to create modal dialogs. When opened with showModal(), the browser takes responsibility for important expected behaviour, including moving focus, keeping keyboard focus within the dialog, and returning focus to the invoking element when the dialog closes.
That removes a large amount of fragile custom code.
A simplified foundation is:
- a real
buttonas the trigger, - a
<dialog>with an accessible name, showModal()for modal opening,- a visible close or cancel action,
close()or a dialog form for clean closure.
Native dialogs are not automatically perfect. The team still has to decide:
- which element receives initial focus,
- whether the dialog scrolls sensibly at high zoom and small viewports,
- how validation errors are handled,
- where focus should go after a completed action,
- how nested dialogs or other overlays behave,
- whether the concrete flow works well in target browsers and assistive technologies.
But the browser starts from a much stronger platform contract than an arbitrary div with position: fixed.
Red flag 4: After closing, the user is “somewhere”
A modal interrupts a workflow. Closing it should resume that workflow.
The normal APG behaviour is therefore to return focus to the element that opened the dialog.
For example:
- The user focuses “Edit address.”
- The dialog opens.
- The user changes the address and saves.
- The dialog closes.
- Focus returns to “Edit address.”
There are sensible exceptions. If the trigger no longer exists or the completed task creates a more logical next step, focus can move elsewhere. After “Add row,” the newly inserted row may be the more useful destination.
What is not useful is letting focus fall back to the body, jump to the top of the document, or disappear entirely.
For keyboard users, that feels like the application briefly loses its memory after every dialog action.
Red flag 5: The X works — with a mouse
Every dialog needs a clear exit.
The APG strongly recommends a visible button to close the dialog, such as a close icon or cancel button. Escape is also part of the expected keyboard interaction for a modal dialog.
Do not test only whether an X is visible:
- Is it a real button?
- Does it have a useful accessible name such as “Close dialog”?
- Is its focus indicator visible?
- Can it be activated with
EnterorSpace? - Does
Escapework? - For critical workflows, is it clear whether closing discards data?
- Are unsaved changes protected when necessary?
A clickable SVG with an event handler is not an equivalent replacement for a semantic button.
A focus trap is not the same as a keyboard trap
The terminology causes avoidable confusion.
A modal dialog is supposed to contain its tab sequence. When focus reaches the last focusable element, pressing Tab should cycle to the first focusable element in the dialog. That is expected modal behaviour.
WCAG 2.1.2, No Keyboard Trap, addresses a different failure: a person must not become stuck in a component without a keyboard route out, or without being told how to leave when a non-standard method is required.
For a modal, the practical interpretation is: focus remains inside while the dialog is in use, but the dialog itself must be dismissible by keyboard. Escape, Cancel, or Close provides the way out.
A custom focus trap that intercepts Tab but loses its closing path after a JavaScript error is exactly the kind of robustness failure a happy-path demo will not reveal.
A five-minute test that finds more than a screenshot
A useful modal audit starts without a mouse.
1. Focus the trigger
Use only Tab to reach the button that opens the dialog. Is focus visible? Can the dialog be opened with the keyboard?
2. Open it and observe focus
Where does focus move? Does that target fit the content and task? Is a title or useful context announced?
3. Tab forward
Move through every interactive element. After the final item, focus must not disappear into the page behind the dialog.
4. Tab backwards
Repeat with Shift+Tab. Custom focus traps often fail in the reverse direction.
5. Test the background
Deliberately try to operate the page behind the dialog using keyboard, pointer, touch, and, where available, screen-reader navigation.
6. Close with Escape
Does the expected keyboard route work? If unsaved changes require confirmation, is that workflow respected?
7. Inspect focus after close
Does it return to the trigger or move to a deliberately selected next step?
8. Test zoom and narrow viewports
At 200 or 400 percent zoom, a compact dialog can become a long document. Are the title, content, and close action still reachable without focus moving into invisible regions?
9. Force error states
Validation, slow requests, API failures, and loading states all change dialog content. Does focus remain logical? Are error messages perceivable? Can the dialog still be closed?
10. Open another overlay
What happens if the dialog launches a date picker, confirmation dialog, or second overlay? Which layer is active? Where does focus return as each layer closes?
These nested states are where a seemingly trivial modal becomes a real component test.
What a design system should define
Modal behaviour should not be reinvented in every feature story. A shared component needs explicit contracts for:
- semantic element and opening mechanism,
- accessible name,
- initial focus rules,
- visible focus indicators,
- keyboard dismissal and visible close action,
- focus restoration,
- scrolling and sizing,
- destructive actions,
- nested overlays,
- keyboard and assistive-technology testing.
Automated regression tests are especially valuable for the basics: open the dialog, assert focus, walk the tab sequence, close it, and verify focus restoration.
Automation does not replace screen-reader or usability testing. It does stop a refactor from silently breaking focus logic that nobody has manually inspected for months.
What Website-Pflichtencheck would inspect
A dialog and focus review can examine:
- native
dialogusage versus custom overlays, showModal()and actual modality,- dialog names and ARIA relationships,
- initial focus placement,
TabandShift+Tabsequences,- visible focus,
Escape, Cancel, and Close controls,- background inertness,
- focus restoration after completion,
- destructive confirmations,
- validation and loading states,
- zoom, mobile viewports, and scrolling,
- nested dialogs, date pickers, and portals,
- design-system regression tests.
The goal is not to wrap every dialog in as much accessibility code as possible. It is almost the opposite: use as much native platform behaviour as possible and as little fragile custom focus logic as necessary.
A modal is a short moment where the application tells the user, “This is what matters right now.” The implementation should therefore know exactly where the user is, what they can operate, and where they return afterwards.
If those answers are not reproducible, the dialog is not finished. It is merely centred.