AI & ML DevOps General Tech Community Best Practices & Tools All News About Contact
advertisement
General Tech

Stop Rebuilding Modals: A Deep Dive into the <dialog> Element

May 2026 8 min read
Back to General Tech

Introduction

For more than a decade, building modals on the web meant reimplementing infrastructure:

- focus traps

- scroll lock

- ARIA wiring

- z-index battles

- extreme cases of accessibility

Frameworks solved it with layers of abstraction, but the browser didn't have a native solution.

That changed.

The <dialogue>

The element is no longer an experimental curiosity. As of 2022-2023, it has become a fully viable modal primitive for all browsers, complete with focus management, top-layer rendering, background handling, and built-in accessibility.

This article explains what <dialog>

really does it, how is it different from show()

and showModal()

, how it compares to <details>

and the Popover API

and when you should (and shouldn't) replace your custom modals with it.

How to implement

<dialog id="myDialog">

<p>Hello world</p>

<button onclick="myDialog.close()">Close</button>

</dialogue>

<button onclick="myDialog.showModal()">Open</button>

JavaScript API

The true power of <dialogue>

It's in your methods:

.showModal()

- sets

open

attribute: promote dialogue to the "upper layer"

- create

::backdrop

- applies

inert

to the rest of the document: exposes the dialog box as

aria-modal="true"

to assistive technology: move the focus

- register the ESC listener

The "top layer" is not only used in dialog boxes, but also in the full screen API, pop-ups, and context menus. It is located above the normal stacking context. That's why the z-index doesn't affect modal dialogs at all: they are rendered on a separate top layer above the normal stacking context.

.show()

- sets

open

attribute: keeps the dialog in the normal layout and in the stacking context

When you open the dialog with show()

there is no inert

, backdrop

, focus trap

, aria-modal

, only the dialog visible.

.close()

- Close the dialogue.

background style

When opened with .showModal()

, the browser creates a background as a special pseudo-element. You design it like this:

dialog::backdrop {

background: rgba(0, 0, 0, 0.5);

}

Common errors when using <dialog>

Although <dialogue>

simplifies modal behavior, there are some bugs that developers frequently run into.

Using open

Instead of showModal()

Settings <open dialog>

o dialog.open = true

it doesn't make the dialogue modal. It just makes it visible.

There is no focus trap, no background, no inert background, and no top layer behavior.

If you need modal behavior, always use dialog.showModal()

.

Forget to label the dialogue

A dialog box without an accessible name is confusing to screen reader users.

Always provide one of the <dialog aria-labelledby="titleId">

o <dialog aria-label="Delete element">

.

If the dialog has no title or label, it will simply be announced as "dialog", which provides no context.

Incorrectly animate open attribute

A closed dialog box is effectively displayed: none

. That means that transitions often don't run when the dialog is first opened.

To animate correctly, you can use @starting-style

(modern CSS), or apply a class on the next animation frame after calling showModal()

.

Waiting for show()

behave like showModal()

show()

it just makes the dialog visible. It doesn't move it to the top layer, it doesn't catch focus, it doesn't create a background, it doesn't make the background inert.

.

If you need to block the interaction, use showModal()

.

Open multiple modal dialogs

Only one modal dialog can be opened at a time. Calling showModal()

in a second dialog while one is already open will throw an error. If you need cumulative interactions, close the current dialog box before opening another one.

Reimplement what the browser already does

Some developers still add their own focus trap, add role="dialog"

manually, add aria-modal="true"

manually, lock the body movement manually.

When showModal() is used

, this is unnecessary and may even conflict with native behavior. Let the platform take care of it.

Accessibility Notes

By default a showModal()

dialogue

- behaves like

role="dialogue"

- has

Related Coverage

General Tech

Top 7 Featured DEV Posts of the Week

General Tech

3 words worth a billion dollars: Drift to Determinism (DriDe)