Overview
The native <dialog> element with ::backdrop gives you focus trapping, Esc dismissal, and inert behavior for content outside the dialog for free — no third-party modal library required. This pattern wraps the native element with a viewport-driven responsive layout that becomes a centered modal on desktop and an edge-anchored side sheet on mobile.
When to use it
Reach for native dialog on any modal interaction — confirmations, settings panels, filter sidebars, login flows. Skip it for non-blocking notifications (use a toast) and for content where the user might need to compare with the underlying page (a non-modal popover is better). On mobile, skip the modal layout and reach for full-screen sheets when the form has more than three inputs.
How it works
Open the dialog with dialog.showModal() — this is the critical call (not show()) because it activates the top layer (which renders above all z-indices), the ::backdrop pseudo-element, the inert tree outside the dialog, focus trapping, and Esc dismissal. Style the open dialog with a media query: on wide viewports center it with margin: auto and a fixed width; on narrow viewports anchor it to the bottom with position: fixed; inset: auto 0 0 0 and a slide-up transform. Animate both the dialog and the ::backdrop with a paired @keyframes rule on open attribute presence.
Production gotchas
The dialog needs an aria-labelledby pointing to its heading, or screen readers announce “dialog” with no further context. Focus does not auto-move to the dialog on open in older Safari; call firstFocusable.focus() after showModal() as a belt-and-suspenders fix. Closing the dialog must return focus to the trigger element — store a reference on open and call trigger.focus() after close(). Animating dialog display from none to block requires @starting-style + the transition-behavior: allow-discrete property in modern browsers; without it the enter animation skips.
Accessibility
Native dialog gets focus trap and inert-outside for free, but you still need to wire the trigger-return path yourself. Mobile users dismissing the side sheet with the backdrop tap should also have an explicit close button focusable from the keyboard. Under prefers-reduced-motion: reduce drop the slide-up transform and use a simple opacity fade, or skip the enter animation entirely. The ::backdrop color should pass 3:1 against the page so the modal feels properly isolated.
References