A Complete Accessible Modal Example
A Complete Accessible Modal Example
Here is a complete example using the native <dialog> element. It includes a clear accessible name, keyboard-friendly controls, native modal behavior, a cancel action, and method="dialog" for closing the dialog without extra JavaScript.
<button type="button" id="openDeleteDialog">
Delete account
</button>
<dialog id="deleteDialog" aria-labelledby="deleteDialogTitle">
<form method="dialog">
<header>
<h2 id="deleteDialogTitle">
Delete your account?
</h2>
</header>
<p>
This will permanently delete your account and all associated data.
This action cannot be undone.
</p>
<div class="dialog-actions">
<button type="submit" value="cancel">
Cancel
</button>
<button type="submit" value="delete">
Delete account
</button>
</div>
</form>
</dialog>
<script>
const openButton = document.querySelector('#openDeleteDialog');
const dialog = document.querySelector('#deleteDialog');
openButton.addEventListener('click', () => {
dialog.showModal();
});
dialog.addEventListener('close', () => {
if (dialog.returnValue === 'delete') {
deleteAccount();
}
});
function deleteAccount() {
console.log('Account deleted');
}
</script>
Why this example is accessible
The important part is not simply using <dialog>. The surrounding HTML and interaction also need to make sense to users navigating with a keyboard or assistive technology.
1. Give the dialog a meaningful name
<dialog id="deleteDialog" aria-labelledby="deleteDialogTitle">
<h2 id="deleteDialogTitle">
Delete your account?
</h2>
</dialog>
The aria-labelledby attribute associates the dialog with its visible heading. This gives the dialog an accessible name without adding another piece of visually hidden text.
For a simple dialog where the heading already provides a clear name, this is often all the additional ARIA you need.
2. Use real buttons
<button type="submit" value="cancel">
Cancel
</button>
<button type="submit" value="delete">
Delete account
</button>
These are actual buttons, so they can be reached and activated using the keyboard. There is no need to turn a <div> into a button with JavaScript.
3. Let the browser handle modal behavior
dialog.showModal();
Using showModal() makes the dialog modal and places it in the browser's top layer. The rest of the document becomes inert while the dialog is open.
The browser also provides native handling for closing a modal dialog with the Escape key.
4. Use method="dialog" for the actions
<form method="dialog">
<button value="cancel">Cancel</button>
<button value="delete">Delete account</button>
</form>
Submitting this form closes the dialog. The button's value becomes the dialog's returnValue.
dialog.addEventListener('close', () => {
console.log(dialog.returnValue);
});
Clicking Cancel produces "cancel", while clicking Delete account produces "delete".
5. Style the backdrop without another overlay element
dialog::backdrop {
background: rgb(0 0 0 / 50%);
}
You do not need to create a separate fixed-position element behind the modal just to provide the backdrop.
Adding a close button
If your dialog has a visible close button, it can also use method="dialog":
<form method="dialog">
<button type="submit" value="cancel" aria-label="Close dialog">
&times;
</button>
<h2>Account settings</h2>
<p>Update your account preferences.</p>
</form>
Prefer a real <button> over a clickable icon or <div>. If the button contains only an icon, give it an accessible name with visible text or an appropriate aria-label.
One important detail: focus
Native <dialog> handles important parts of modal interaction for you, but you should still test the actual experience. In particular, check where focus goes when the dialog opens and where it returns after the dialog closes.
For many simple dialogs, the browser's native behavior is sufficient. If your application needs a specific focus target or a more complex interaction, you can manage that behavior explicitly.
For example, you can focus a particular control after opening:
dialog.showModal();
dialog.querySelector('button').focus();
Do this only when you have a reason to control the initial focus. Avoid adding JavaScript simply because a modal is involved.
Modal without a javascript library
Comments
No comments yet. Be the first to share your thoughts.