dialog
Modal and drawer. Focus trap, Esc, and top layer — the browser's. Open/close — invoker commands (shimmed while support spreads).
Example
Form drawer
On mobile, open the keyboard and move between fields. The body scrolls while the footer stays available.
Basic usage
<button commandfor="confirm" command="show-modal">Open</button> <!-- trigger -->
<dialog id="confirm"> <!-- content -->
<button class="close" commandfor="confirm" command="close" aria-label="Close">✕</button>
<header> <!-- header -->
<h2>Are you sure?</h2> <!-- title -->
<p>This cannot be undone.</p> <!-- description -->
</header>
<p>Any body content.</p>
<footer> <!-- footer: right-aligned row -->
<form method="dialog">
<button>Cancel</button>
<button data-variant="primary" value="ok">Confirm</button>
</form>
</footer>
</dialog>
API
Attributes
| Name | Type | Default | Description |
|---|---|---|---|
data-avoid-keyboard | false | automatic | On a drawer, disables the optional module’s keyboard positioning when your application manages it. |
data-drawer | boolean | false | Turns the dialog into a responsive desktop side sheet and mobile bottom sheet. |
open | native boolean | false | Reflects whether the native dialog is open. Prefer showModal() to open a modal. |
Content/markup contract
| Part | Contract | Default | Description |
|---|---|---|---|
header | direct child | optional | Contains the title and description. |
| Body | one element between header and footer | — | Padded, scrollable content region. |
footer | direct child | optional | Right-aligned actions; drawer actions stack and pin to the bottom. A form method="dialog" closes natively. |
button.close | direct child | optional | Corner dismiss control; provide an accessible name. |
Native methods and values
| Name | Type | Default | Description |
|---|---|---|---|
showModal() | method | — | Opens in the top layer with modal focus and backdrop behavior. |
close() | method | — | Closes the dialog. |
returnValue | string property | "" | Carries the value of the button that submitted a method="dialog" form. |
drawer.js
Optional. Below 40rem the drawer is a content-height, full-width bottom sheet. The module adds a grab handle and swipe-to-dismiss from the handle/header strip: downward drags follow the pointer, upward drags resist, and the sheet closes past one third of its height or on a flick. It also manages the mobile scrim, lightening it during drag and fading it on close, including on iOS. Without the module, open/close, Esc, and the CSS-only scrim still work; no handle appears.
<script type="module" src="mica/drawer.js"></script>While editing in a mobile drawer, the module fits the sheet above the software keyboard, keeps its body scrollable, and reveals the focused field. It restores authored positioning when the keyboard closes and leaves pinch zoom to the browser. Use data-avoid-keyboard="false" if your application already manages keyboard positioning. This requires the browser’s Visual Viewport API; without it, native behavior remains.
On mobile the handle is the dismiss affordance, so you may hide the redundant corner close button:
@media (max-width: 40rem) {
dialog[data-drawer] > button.close { display: none; }
}invoker.js
Temporary optional shim for the recipe's show-modal and close invoker commands. It installs nothing when commands are native.
<script type="module" src="mica/invoker.js"></script>Accessibility
Native modal dialogs provide top-layer behavior, focus trapping, Esc dismissal, and backdrop handling. Give icon-only close buttons an accessible name. Use a heading and description in the header so the purpose is clear.
Browser support
Invoker commands (commandfor/command) are Baseline newly-available (Chrome 135+, Firefox 144+, Safari 26.2+). Include invoker.js until they are widely available. The shim's removal is a one-line diff.
Notes
Enter/exit animations use @starting-style and transition-behavior: allow-discrete. Standard modal backdrops fade where the engine supports top-layer exit transitions.