Skip to main content

Focus management after a modal: a WCAG guide

A dialog can have perfect markup and still leave a keyboard user stranded behind it. This guide covers where focus should go at each moment of a modal's life, the WCAG criteria involved, and how to test it step by step.

Last updated

Where should focus go when a modal opens and closes?

When a modal opens, move focus into it — usually to the first interactive element. While it is open, keep Tab inside the dialog and make the page behind it inert. Let Escape close it. When it closes, return focus to the element that opened it, unless that element is gone or another one makes the next step more logical.

Why focus management is the whole job

A sighted mouse user sees an overlay appear and clicks inside it. A keyboard or screen-reader user only knows what has focus. If focus stays on the button behind the overlay, the next Tab moves through the page they can no longer see; if focus jumps to the top of the page on close, they have to find their place again from the beginning.

WCAG's own example for 2.4.3 Focus Order describes the correct behaviour: “When the trigger button is activated, a dialog opens and focus is set within the dialog. As long as the dialog is open, all web page content outside the dialog becomes inert and cannot receive focus… When the dialog is dismissed, focus returns to the button or the element following the button” ( W3C Web Accessibility Initiative, 2026).

The four moments of a modal

1. On open: move focus in

The Authoring Practices dialog pattern says focus typically moves to the first interactive element, and recommends alternatives when that would be wrong (WAI-ARIA Authoring Practices Guide, 2026):

  • For semantically complex content — lists, tables, several paragraphs — put tabindex="-1" on a static element at the start and focus that.
  • If focusing the first control would scroll the start of the content out of view, focus a static element near the top.
  • For a dialog confirming an irreversible action, focus the least destructive option.
  • For an information-only dialog, focus the control people use most, such as OK or Continue.

The container needs role="dialog", aria-modal="true", and a name from aria-labelledby pointing at its visible title (or aria-label), so the screen reader announces what opened — the 4.1.2 Name, Role, Value requirement (W3C Web Accessibility Initiative, 2026).

2. While open: keep focus inside

Per the pattern, Tab on the last tabbable element moves focus to the first, and Shift+Tab on the first moves it to the last. Content outside the dialog should be inert. The inert attribute makes content unfocusable, unclickable and excluded from the accessibility tree (MDN Web Docs, 2026).

Containing focus is not a keyboard trap. The Understanding document for 2.1.2 says restricting focus “when the user is inside a modal dialog or popover” does not fail the criterion “as long as the user knows how to ‘untrap’ the focus and leave that component” (W3C Web Accessibility Initiative, 2026). And a properly built modal passes 2.4.11 Focus Not Obscured, because “the dialog takes focus on appearance” (W3C Web Accessibility Initiative, 2026).

3. Closing: Escape and a visible button

Escape closes the dialog in the Authoring Practices pattern. MDN also recommends an explicit close button, calling it “the most robust way to ensure that all users can close the dialog” (MDN Web Docs, 2026).

4. After close: return focus

“When a dialog closes, focus returns to the element that invoked the dialog unless” that element no longer exists or the workflow makes another element a better next step (WAI-ARIA Authoring Practices Guide, 2026). Deleting a row from a confirmation dialog, for example, removes the trigger — move focus to the neighbouring row or to the list, not to the top of the page.

The native dialog element

showModal() does much of this for you: it places the dialog in the top layer, makes everything outside it inert, implicitly sets aria-modal="true", sets focus on the first nested focusable element, and lets Escape close it (MDN Web Docs, 2026). MDN recommends autofocus to choose the initial element deliberately, and warns against putting tabindex on the <dialog> itself. It does not promise that focus returns to the trigger on close, so restore it explicitly:

<button type="button" id="open-filters">Filters</button>

<dialog id="filters" aria-labelledby="filters-title">
  <h2 id="filters-title">Filter results</h2>
  <!-- form controls -->
  <button type="button" id="close-filters" autofocus>Close</button>
</dialog>

<script>
  const dialog = document.getElementById('filters');
  const trigger = document.getElementById('open-filters');

  trigger.addEventListener('click', () => dialog.showModal());
  document.getElementById('close-filters')
    .addEventListener('click', () => dialog.close());

  // Fires for Escape and for close(), so every path restores focus.
  dialog.addEventListener('close', () => trigger.focus());
</script>

A custom modal has to do each of those things by hand: the role and name, moving focus in, wrapping Tab, setting inert on the rest of the page, handling Escape, and restoring focus.

The WCAG criteria involved

WCAG 2.2 success criteria relevant to modal dialog focus management
Success criterionLevelWhat it means for a modal
2.4.3 Focus OrderAFocus moves into the dialog when it opens, stays there while it is open, and goes back to the trigger when it closes.
2.1.2 No Keyboard TrapAKeeping Tab inside an open modal is allowed, as long as the keyboard user can close it and leave.
2.4.11 Focus Not Obscured (Minimum)AAA modal that takes focus passes; an overlay that covers the focused element without taking focus can fail.
4.1.2 Name, Role, ValueAThe dialog is exposed as a dialog, with a name, so a screen reader announces what just opened.

How to test modal focus management, step by step

  1. Open the dialog from the keyboard

    Tab to the control that opens the dialog and press Enter or Space. Note which element it is — you will check focus comes back to it.

  2. Check where focus landed

    Focus should be visibly inside the dialog, and a screen reader should announce that a dialog opened and its name. Focus left on the trigger behind the overlay, or on the page body, is a failure.

  3. Tab forward and backward through the dialog

    Press Tab past the last control and Shift+Tab past the first. Focus should wrap within the dialog and never reach links or buttons in the page behind it.

  4. Try to reach the page behind

    With a screen reader, try to read or activate content outside the dialog. While a modal is open, the rest of the page should be inert: not focusable, not clickable, and not exposed to assistive technology.

  5. Press Escape

    Escape should close the dialog. If a dialog cannot be closed from the keyboard at all, it is a keyboard trap.

  6. Check where focus returned

    After closing, focus should be on the control that opened the dialog — or, if that control no longer exists, on the element that makes the next step logical. Focus at the top of the page is a failure.

  7. Repeat for every way the dialog closes

    Close it again with the close button, a Cancel button and a successful submit. Each path should leave focus on the trigger, or somewhere that makes the next step logical.

The same keyboard habits apply to the rest of the site — see how to test keyboard-only navigation.

What automation can catch

A static scan reads the page before anyone opens the dialog, so it can check the dialog's markup but not where focus goes — that only exists once someone presses the button.

In Accessible, journey testing checks the focus state after every interaction in a journey, so a cart drawer that opens without taking focus, or a dialog that dumps focus at the top of the page on close, is reported at the step it happened. Real user monitoring adds two signals from real sessions: focus traps that keyboard users cannot leave, and focus lost to the page body after a single-page-app route change. Dialogs and drawers are common in the flows where getting this wrong costs the most — see checkout accessibility and single-page apps.

Sources

  1. Dialog (Modal) Pattern (opens in a new tab). WAI-ARIA Authoring Practices Guide, Accessed September 14, 2026.
  2. Understanding Success Criterion 2.4.3: Focus Order (opens in a new tab). W3C Web Accessibility Initiative, Accessed September 14, 2026.
  3. Understanding Success Criterion 2.1.2: No Keyboard Trap (opens in a new tab). W3C Web Accessibility Initiative, Accessed September 14, 2026.
  4. Understanding Success Criterion 2.4.11: Focus Not Obscured (Minimum) (opens in a new tab). W3C Web Accessibility Initiative, Accessed September 14, 2026.
  5. Understanding Success Criterion 4.1.2: Name, Role, Value (opens in a new tab). W3C Web Accessibility Initiative, Accessed September 14, 2026.
  6. <dialog>: The Dialog element (opens in a new tab). MDN Web Docs, September 2, 2026.
  7. HTML inert global attribute (opens in a new tab). MDN Web Docs, Accessed September 14, 2026.

Frequently asked questions

Where should focus go when a modal dialog opens?

Into the dialog. The WAI-ARIA Authoring Practices say focus typically moves to the first interactive element, but recommend a static element at the start of the content (with tabindex="-1") when the dialog holds complex content or would scroll its beginning out of view, and the least destructive option when the dialog confirms an irreversible action.

Should focus return to the button that opened the modal?

Yes, unless that button no longer exists or a different element makes the workflow more logical. The Authoring Practices state that when a dialog closes, focus returns to the element that invoked it, with exactly those two exceptions.

Is trapping focus inside a modal a WCAG failure?

No, provided the user can leave. The Understanding document for 2.1.2 No Keyboard Trap says restricting focus inside a modal dialog does not fail the criterion as long as the user knows how to close it. A modal with no keyboard way out does fail.

Does the native <dialog> element handle focus for me?

Much of it. showModal() places the dialog in the top layer, makes the rest of the page inert, sets focus on the first focusable element inside it and lets Escape close it. Use autofocus to choose the initial element deliberately, and store and restore focus to the trigger yourself on close.

How do I make the page behind a custom modal inert?

Add the inert attribute to the content outside the dialog while it is open, and remove it on close. Inert content cannot be focused or clicked and is excluded from the accessibility tree. With showModal(), the browser does this for you.

See where focus goes in your real flows.

A screen-reader journey checks focus after every step, and monitoring catches traps in real sessions.