# A modal opens while focus stays behind it

Source: https://easeweb.dev/learn/modal-focus-management
Topics: Dialogs and overlays > Focus moves into a dialog and back out (WCAG 2.4.3, 2.1.2); Dialogs and overlays > Content behind a dialog cannot be reached (WCAG 2.4.3)
The fix: Prefer a native dialog opened with showModal(), give it an accessible name, choose suitable initial focus, and verify focus restoration. A custom modal needs equivalent behavior.
Test with: keyboard, screen reader, zoom, forced colors
References: https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/

This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.

## What happens

Activate Edit details. A dialog covers the page. Press Tab: focus moves through navigation links behind the overlay instead of entering the dialog.

## Why it fails

The visual layer changed without establishing a modal interaction. The user can reach hidden background actions and may not encounter the dialog heading.

## Who is affected

A keyboard or screen-reader user may continue editing the wrong context, miss required input, or lose their place after closing.

## What automation and AI miss

Correct `role` and `aria-modal` attributes do not move focus or prevent background interaction. A static DOM snapshot cannot prove opening, containment, dismissal, and restoration across the actual workflow.

## Before: an overlay that only looks modal

```html
<div class="overlay" hidden>
  <div class="modal">
    <p class="modal-title">Edit details</p>
    <button onclick="closeModal()">Close</button>
  </div>
</div>
```

```js
function openModal() {
  document.querySelector('.overlay').hidden = false;
}
```

Showing the overlay changes what people see, nothing else. Focus stays on the trigger, Tab continues through the page behind, Escape does nothing, and the dialog has no name.

## After: a native dialog

```html
<button type="button" id="edit-trigger">Edit details</button>

<dialog id="edit-dialog" aria-labelledby="edit-title">
  <h2 id="edit-title">Edit details</h2>
  <form method="dialog">
    <label for="display-name">Display name</label>
    <input id="display-name" name="name">
    <button value="save">Save</button>
    <button value="cancel">Cancel</button>
  </form>
</dialog>
```

```js
const trigger = document.getElementById('edit-trigger');
const dialog = document.getElementById('edit-dialog');
trigger.addEventListener('click', () => dialog.showModal());
```

`showModal()` moves focus into the dialog, makes the page behind it inert, and closes the dialog on Escape. Buttons in a `method="dialog"` form close it too. Current browsers return focus to the element that opened it; still verify this, because a re-rendered page may have replaced that element.

```js
// If the trigger no longer exists after closing, choose where focus goes.
dialog.addEventListener('close', () => {
  if (!trigger.isConnected) document.getElementById('item-list')?.focus();
});
```

## Implementation decisions

A long dialog may need initial focus on a heading with `tabindex="-1"` so its beginning stays visible. Destructive confirmations should put initial focus on the least destructive action; use `autofocus` on that button. Avoid nesting dialogs until their restoration behavior is tested.

If you cannot use the native element, a custom modal needs the same behavior: `role="dialog"` with `aria-modal="true"` and a name, focus moved in on open, the rest of the page made `inert`, Escape to close, and focus returned on close.

## Verify the fix

1. Open the dialog from the keyboard; focus must enter it at a useful point.
2. Tab forward and backward; background controls must remain unavailable.
3. Close with Escape and with the visible close button, in separate runs.
4. Confirm focus returns to the trigger, or to an appropriate control if the trigger is gone.
5. Confirm a screen reader announces the dialog name on open, and check the dialog at increased zoom and in forced colors.

Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the shared dialog component changes.

## Limits

This is a starting pattern, not a guarantee for every application. The snippets omit saving and validation. Test the complete journey with your target assistive technology.
