Archives

MY APPROACH

The right solution, not the biggest solution.

You don't need to know whether your problem needs an API, an automation workflow, a dashboard or a custom application.

Tell me the problem. I'll help figure out a practical way to solve it.

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">
    &amp;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.

 

 

Comments

No comments yet. Be the first to share your thoughts.

Leave a comment

RDA
Rashid Anwar
Independent Developer

I’m Rashid, an independent developer focused on practical software solutions for businesses.

HAVE A PROJECT IN MIND?

Let's talk about the problem you're trying to solve.

Share what you need. We can discuss the requirements and a practical next step.

Start a Conversation