Modals
Modals (also know as dialogs) can require a lot of work to be accessible. However, there is a new element dialog
which takes care of a lot of these issues, but it is not 100% backwards compatible.
Can I Use - dialog support statistics
An example dialog
Modals must be accessible for both 1 keyboard users and 2 screen-reader users.
1 Keyboard users
The modal should be opened and closed with a keyboard and have focus (technically, :focus-visible) states for any button
that opens/closes the modal.
After closing the modal, the focus should return to the button
which opened it - either when the user presses ESC or clicks the close button
.
When the modal is open, the rest of the page should not be accessible via a keyboard i.e. it should have a focus trap.
Focus traps explained
To achieve a focus trap, it can be marked as either as inert
or all interactive elements can be marked with tabIndex="-1"
or a focus trap could be used to capture TAB key events and prevent the user from tabbing to elements outside of the modal.
The user should be able to press ESC to close the modal.
2 Screen-reader users
The modal should inform the user when it opens that it is a modal/dialog and the user’s focus should be taken to the modal upon opening.
The rest of the page (i.e. the content 'underneath' the modal) should not be accessible to a screen-reader. It can be marked as either aria-hidden="true"
or inert
.
See Hiding elements accessibly for further information.
General UX (user experience)
The user should be able to click outside of the modal e.g. on the background to close the modal. Although this is not techncially an accessibility requirement.
_Note:_ If the user clicks into the background of the app to close the modal, focus does not have to return to the opening button and can return to `body`.
Note: The dialog element does not close itself when the user clicks outside of it.
Dialog element code example
The following code example shows a dialog
element which is automatically shown after 500 milliseconds.
<div aria-labelledby="dialog-title" id="dialog">
<h2 id="dialog-title">An alert modal</h2>
<p>Some generic copy.</p>
<button type="button" onClick="javascript:closeDialog()">Close</button>
</div>
<script>
// Use JavaScript to show the modal dialog on first page load
/** @var {HTMLElement | null} */
const dialog = document.querySelector(".dialog");
/** @var {number} */
const DIALOG_DELAY_IN_MS = 500;
// Show dialog after N seconds
setTimeout(() => {
dialog.showModal();
}, DIALOG_DELAY_IN_MS);
const closeDialog = () => {
dialog?.close();
};
</script>