focus-on-modal-open
Web validation
When a modal dialog opens, focus should move into it so users can start working with the dialog right away. Usually, focus goes to the first focusable element inside the dialog. If the dialog has no focusable elements, or if its first focusable element comes after some of the dialog's content, focus can instead go to an element at the top of the dialog, such as its heading, or to the dialog itself. If focus stays behind on the page, keyboard users may lose sight of the focus indicator, which can be hidden by the dialog or by the overlay that dims the page. They then have to press Tab repeatedly to reach the dialog's place in the page's tab sequence. Screen reader users may get no indication at all that the dialog has opened.
Pass Patterns
Focus moves to the first focusable element
When the dialog opens, the script moves focus to its first focusable element.
<button id="open-settings">Settings</button>
<div role="dialog" aria-modal="true" aria-labelledby="settings-title" hidden>
<h2 id="settings-title">Settings</h2>
<label for="display-name">Display name</label>
<input id="display-name" type="text" />
<button>Save</button>
</div>
<script>
document.getElementById("open-settings").addEventListener("click", () => {
document.querySelector('[role="dialog"]').hidden = false;
document.getElementById("display-name").focus();
});
</script>Focus moves to the dialog heading when content comes first
The dialog starts with text that users need to read before its buttons, so focus moves to the heading, made focusable with tabindex="-1", instead of skipping ahead to the buttons.
<div role="dialog" aria-modal="true" aria-labelledby="terms-title">
<h2 id="terms-title" tabindex="-1">Terms of use</h2>
<p>Please read the following terms before continuing…</p>
<button>Decline</button>
<button>Accept</button>
</div>
<script>
document.getElementById("terms-title").focus();
</script>Native <dialog> opened with showModal()
A native <dialog> opened with showModal() moves focus into the dialog automatically.
<button id="open-help">Help</button>
<dialog id="help-dialog" aria-labelledby="help-title">
<h2 id="help-title">Help</h2>
<form method="dialog">
<button>Close</button>
</form>
</dialog>
<script>
document.getElementById("open-help").addEventListener("click", () => {
document.getElementById("help-dialog").showModal();
});
</script>Fail Patterns
Focus stays on the page behind the dialog
The dialog is shown, but focus is never moved into it, so it stays on the "Settings" button behind the dialog.
<button id="open-settings">Settings</button>
<div role="dialog" aria-modal="true" aria-labelledby="settings-title" hidden>
<h2 id="settings-title">Settings</h2>
<button>Close</button>
</div>
<script>
document.getElementById("open-settings").addEventListener("click", () => {
document.querySelector('[role="dialog"]').hidden = false;
// Focus is not moved into the dialog
});
</script>Try It Yourself
Using a keyboard
- Move focus to the control that opens the modal dialog, and press Enter or Space to open it.
- Watch where the focus indicator is.
- Press Tab once.
Expected results: Focus is inside the dialog when it opens, and Tab moves between the dialog's controls.
Actual results: Focus stays on the page behind the dialog, is hidden by the overlay, or Tab moves through the page before reaching the dialog.
Using a screen reader
New to screen reader testing, or need a refresher?
Review this article first: Practical guides for using screen readers
- Load the page and open a screen reader.
- Open the modal dialog.
- Listen to what the screen reader announces.
Expected results: The screen reader announces the dialog, for example its name and role, and then the focused element inside it.
Actual results: The screen reader announces nothing new, or only the control that opened the dialog, so the user doesn't know the dialog is open.
FAQ
How does this issue relate to 2.4.3 Focus Order?
SC 2.4.3 requires that focusable components receive focus in an order that preserves meaning and operability. When a dialog opens, it becomes the only meaningful place to work, so focus should move to it. If focus stays behind on the page, the focus order no longer follows the user's task, and users must search for the dialog before they can continue.
Why is this issue marked as Serious?
When focus does not move into an open dialog, keyboard users may lose track of focus entirely and screen reader users may not realize the dialog has opened. This can stop users from completing the dialog's task, so it is rated serious.
Which element should receive focus when the dialog opens?
Usually the first focusable element inside the dialog. If the dialog has no focusable elements, or if important content comes before its first focusable element, move focus to an element at the top of the dialog, such as its heading (with tabindex="-1"), or to the dialog container itself. For dialogs that confirm destructive actions, it is common to focus the least destructive option, such as "Cancel".
Do native dialog elements handle this automatically?
Yes. A native <dialog> opened with showModal() moves focus into the dialog automatically. Custom modals built with role="dialog" need a script to move focus when they open.
Standards and Regulations
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.4.3 Focus Order (A) | If a web page can be navigated sequentially and the navigation sequences affect meaning or operation, focusable components receive focus in an order that preserves meaning and operability. |
| EN 301 549 | 9.2.4.3 Focus Order | Same as the WCAG text. |
| 508 Trusted Tester | 2.4.3-focus-order-reveal, 4.G | Focus is moved to revealed content. |