Skip to content
Documentation

Overlays

Dialog, Drawer

A modal dialog for focused tasks and confirmations, and a drawer that slides in from the side.

Needs dsgn.js 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.

Delete invoice 2026-114?

The invoice and its payment history will be removed. This cannot be undone.

A confirmation dialog
<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() or command="show-modal"), so the page behind becomes inert and focus stays inside.
  • Name it with aria-labelledby and give the close button an aria-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

AttributeValues and notes
data-sizes | 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