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
Implementation depth
The responsive side sheet should stay a dialog first. showModal, backdrop, Escape behavior, and focus containment define the interaction; the sheet animation only changes how the dialog enters at different widths.
Mobile layout changes need the same dismissal and focus rules as desktop. Test the small viewport sheet, large viewport modal, and reduced-motion open state so the component does not fork into two behaviors.