Documentation
Overlays
Dialog, Drawer
A modal dialog for focused tasks and confirmations, and a drawer that slides in from the side.
components/dialog.css
Example
A button with command="show-modal" opens the dialog named in commandfor, without any script. closedby="any" also closes it with Escape or a click outside. Open it and try the keyboard: focus moves into the dialog, stays there, and returns to the button when it closes.
<button class="dsgn-button" data-intent="danger" type="button" command="show-modal" commandfor="del">Delete invoice</button>
<dialog class="dsgn-dialog" id="del" aria-labelledby="del-title" closedby="any">
<header class="dsgn-dialog-header">
<h2 class="dsgn-dialog-title" id="del-title">Delete invoice 2026-114?</h2>
<button class="dsgn-icon-button" data-variant="ghost" data-intent="neutral" type="button" command="close" commandfor="del" aria-label="Close">
<span class="dsgn-icon" aria-hidden="true">close</span>
</button>
</header>
<div class="dsgn-dialog-body">
<p>The invoice and its payment history will be removed. This cannot be undone.</p>
</div>
<footer class="dsgn-dialog-footer">
<button class="dsgn-button" data-variant="ghost" data-intent="neutral" type="button" command="close" commandfor="del">Cancel</button>
<button class="dsgn-button" data-intent="danger" type="button" command="close" commandfor="del">Delete</button>
</footer>
</dialog>Parts
| Part | Element | Notes |
|---|---|---|
dsgn-dialog |
<dialog> |
Needs an id for the buttons and aria-labelledby pointing at the title. |
dsgn-dialog-header |
<header> |
The title and a close button. |
dsgn-dialog-title |
<h2> |
Names the dialog for screen readers. |
dsgn-dialog-body |
<div> |
The content. It scrolls when it is taller than the window. |
dsgn-dialog-footer |
<footer> |
The actions, the main one last. |
Size
data-size sets the width: s 400 px, m 560 px (default) or l 720 px. A dialog is never wider than the window.
Without invoker commands
command and commandfor are new in browsers. dsgn.js adds the same behaviour where they, or closedby, are not supported yet. To open a dialog from your own script, call showModal() on it.
Accessibility
- Always open the dialog as modal (
showModal()orcommand="show-modal"), so the page behind becomes inert and focus stays inside. - Name it with
aria-labelledbyand give the close button anaria-label. - Ask in the title, answer in the buttons: "Delete invoice?" with Delete and Cancel reads better than OK.
- For a destructive action, make the main button
data-intent="danger".
Usage
Native <dialog> opened with showModal(): focus moves in and is trapped, Escape closes, the page behind is inert. command / commandfor open and close it without JS; dsgn.js adds the same behaviour where invoker commands or closedby are not supported yet.
Attributes
| Attribute | Values and notes |
|---|---|
data-size | s | m (default) | l → 400 / 560 / 720 px, never wider than the viewport. |
Details
- Classes
.dsgn-dialog.dsgn-dialog-header.dsgn-dialog-title.dsgn-icon-button.dsgn-dialog-body.dsgn-dialog-footer- Tokens used
--dsgn-blur-backdrop--dsgn-border--dsgn-border-width--dsgn-control-gap--dsgn-control-height--dsgn-control-pad-y--dsgn-duration--dsgn-ease--dsgn-focus--dsgn-focus-offset--dsgn-focus-width--dsgn-font-sans--dsgn-font-size-body-s--dsgn-font-size-heading-s--dsgn-font-weight-semibold--dsgn-inset--dsgn-line-height-body-s--dsgn-line-height-heading-s--dsgn-radius-l--dsgn-scrim--dsgn-shadow-overlay--dsgn-space-16--dsgn-space-40--dsgn-space-48--dsgn-space-8--dsgn-stack--dsgn-surface-raised--dsgn-text- In Figma
- Dialog · Modal example · Drawer
- Import alone
dsgn.systems/components/dialog.css