Dialog

Material Design 3 dialog component using native HTML dialog element for accessible modal interactions

Modal / Dialog

Modal and Dialog use only the native HTML <dialog> element. Dialog styles <dialog class="dialog"> and relies on showModal() semantics for background inertness, focus containment, Escape dismissal, and focus restoration. The Modal component uses the same native behavior with .modal and .modal-box presentation classes. Custom div overlays, visibility classes, and custom focus traps are not supported.

Why Native Dialog?

Opening native <dialog> with showModal() provides:

  • Modal focus behavior - The browser moves focus into the dialog, contains sequential focus, and restores focus when appropriate
  • Native backdrop - Via ::backdrop pseudo-element
  • Background inertness - Content outside the modal is not interactive
  • Escape dismissal - The browser handles the dialog cancel action

Use the Invoker Commands API (command + commandfor) where supported, or call showModal() and close() from application code. Setting the open attribute, changing CSS visibility, or adding aria-modal does not create equivalent modal behavior.

Basic Usage

A basic dialog consists of a <dialog> element with inner structure for header, body, and footer. Open it with command="show-modal" and close it with command="close", both pointing at the dialog id via commandfor. Prefix demo IDs with demo- so they do not collide with heading IDs.

Basic Dialog

Dialog Title

This is the dialog content. You can add any content here.

Size Variants

Small Dialog

Perfect for simple confirmations and alerts.

Small Dialog

Small Dialog

This is a small dialog (max-width: 20rem).

Large Dialog

For dialogs with more content.

Large Dialog

Large Dialog

This is a large dialog (max-width: 40rem). It provides more space for complex content like forms, tables, or detailed information.

Extra Large Dialog

For complex forms or detailed content.

Extra Large Dialog

Extra Large Dialog

This is an extra large dialog (max-width: 56rem). Use it for complex workflows, data tables, or multi-step forms.

Fullscreen Dialog

Covers the entire viewport, ideal for mobile experiences or immersive content.

Fullscreen Dialog

Fullscreen Dialog

This dialog covers the entire screen with no border radius. Perfect for mobile experiences or content that needs maximum space.

Dialog Variants

Alert Dialog

Simplified dialog for confirmations and critical actions.

Alert Dialog

Delete Item?

Are you sure you want to delete this item? This action cannot be undone.

Dialog with Dividers

Add visual separation between sections using the dialog-divider class.

Dialog with Dividers

Settings

The header and footer have visible divider lines for clear section separation.

Right-Aligned (Default)

Right-Aligned Footer

Right-Aligned Actions

Footer actions are right-aligned by default.

Left-Aligned Footer

Left-Aligned Actions

Use dialog-footer-start for left-aligned actions.

Center-Aligned Footer

Center-Aligned Actions

Use dialog-footer-center for centered actions.

Space-Between Footer

Space-Between Actions

Use dialog-footer-between to space actions apart.

Scrollable Dialog

For dialogs with long content, add the dialog-scrollable class:

Scrollable Dialog

Terms and Conditions

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident.

Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis.

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.

Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore.

Position Variants

Top Position

Top Positioned Dialog

Top Dialog

This dialog appears at the top of the viewport.

Bottom Position

Bottom Positioned Dialog

Bottom Dialog

This dialog appears at the bottom of the viewport.

JavaScript Integration

Open and close buttons may use the Invoker Commands API (command / commandfor). Use showModal() as the application fallback and for programmatic control. Do not substitute dialog.open = true or setAttribute('open', ''): those open a non-modal surface without modal focus or inertness.

// Get dialog reference
const dialog = document.querySelector('dialog');

// Open as a real modal (with backdrop and background inertness)
dialog.showModal();

// Open as non-modal (no backdrop)
dialog.show();

// Close dialog — prefer command="close" on buttons
dialog.close();

// Check if open
if (dialog.open) {
  console.log('Dialog is open');
}

// Listen for close event
dialog.addEventListener('close', () => {
  console.log('Dialog was closed');
});

// Optional policy: close on backdrop click (not a native default)
dialog.addEventListener('click', (e) => {
  if (e.target === dialog) {
    dialog.close();
  }
});

Backdrop Click Example

Backdrop click dismissal is opt-in. Some destructive or multi-step dialogs should require an explicit action instead; do not attach this handler globally.

Backdrop Click Example

Interactive Dialog

Open and close buttons use Command API. JavaScript only handles backdrop click.

Framework Examples

React

React 19 maps the HTML commandfor attribute to the commandFor prop.

interface DialogProps {
  title: string;
  children: React.ReactNode;
  onClose?: () => void;
}

export function Dialog({ title, children, onClose }: DialogProps) {
  const dialogId = 'demo-dialog-react';

  return (
    <>
      <button className="btn btn-primary" command="show-modal" commandFor={dialogId}>
        Open Dialog
      </button>
      <dialog
        id={dialogId}
        className="dialog"
        onClose={onClose}
        onClick={(e) => e.target === e.currentTarget && e.currentTarget.close()}
      >
        <div className="dialog-box">
          <div className="dialog-header">
            <h2 className="dialog-title">{title}</h2>
            <button className="dialog-close" command="close" commandFor={dialogId} aria-label="Close">
              ×
            </button>
          </div>
          <div className="dialog-body">{children}</div>
          <div className="dialog-footer">
            <button className="btn btn-ghost" command="close" commandFor={dialogId}>Cancel</button>
            <button className="btn btn-primary" command="close" commandFor={dialogId}>Confirm</button>
          </div>
        </div>
      </dialog>
    </>
  );
}

Vue

<template>
  <button class="btn btn-primary" command="show-modal" commandfor="demo-dialog-vue">Open Dialog</button>
  <dialog id="demo-dialog-vue" class="dialog" @click.self="$event.currentTarget.close()">
    <div class="dialog-box">
      <div class="dialog-header">
        <h2 class="dialog-title">{{ title }}</h2>
        <button class="dialog-close" command="close" commandfor="demo-dialog-vue" aria-label="Close">×</button>
      </div>
      <div class="dialog-body">
        <slot />
      </div>
      <div class="dialog-footer">
        <button class="btn btn-ghost" command="close" commandfor="demo-dialog-vue">Cancel</button>
        <button class="btn btn-primary" command="close" commandfor="demo-dialog-vue">Confirm</button>
      </div>
    </div>
  </dialog>
</template>

<script setup>
defineProps({ title: String });
</script>

Best Practices

Accessibility

Native modal opening handles much of the interaction model, while authors still own content and initial-focus choices:

  • Focus trapping - Focus is trapped within the modal when open
  • Escape key - Pressing Escape closes the dialog
  • Focus restoration - Focus returns to the trigger element when closed
  • Accessible name - Supply aria-labelledby or aria-label
  • Initial focus - Use autofocus on the most suitable control when the browser’s default first-focusable choice is not appropriate

Add descriptive labels for additional accessibility:

<dialog class="dialog" id="demo-dialog-a11y" aria-labelledby="demo-dialog-a11y-title" aria-describedby="demo-dialog-a11y-desc">
  <div class="dialog-box">
    <div class="dialog-header">
      <h2 class="dialog-title" id="demo-dialog-a11y-title">Confirm Action</h2>
    </div>
    <div class="dialog-body">
      <p id="demo-dialog-a11y-desc">Are you sure you want to proceed?</p>
    </div>
  </div>
</dialog>

User Experience

  1. Use dialogs sparingly - Don’t interrupt the user unnecessarily
  2. Keep content focused - Present one task or decision at a time
  3. Provide clear actions - Use descriptive button labels
  4. Enable easy dismissal - Escape is built in; add a small script if you want backdrop click to close
  5. Consider mobile - Use fullscreen variant on small screens

Background inertness and scroll locking

showModal() makes the rest of the document inert; that is the modal interaction boundary. Page scroll locking is a separate application policy. Dialog CSS constrains and scrolls long .dialog-body content but does not add a global body overflow class. If your product requires the page itself not to scroll, add and remove that state in the same application lifecycle that calls showModal() and handles close/cancel.

Browser requirements

Native <dialog> and showModal() are the baseline. Invoker Commands are progressive enhancement: when command="show-modal" is unavailable, attach a click handler that calls the target dialog’s showModal(). Never fall back to only setting open for a modal task.

API Reference

Class Names

Class Description
.dialog Base dialog styles (apply to <dialog> element)
.dialog-box Inner container for flex layout
.dialog-header Header section with title and close button
.dialog-title Dialog title text
.dialog-close Close button in header
.dialog-body Content section
.dialog-footer Footer with action buttons
.dialog-sm Small dialog (max-width: 20rem)
.dialog-lg Large dialog (max-width: 40rem)
.dialog-xl Extra large dialog (max-width: 56rem)
.dialog-fullscreen Fullscreen dialog
.dialog-top Position at top of viewport
.dialog-bottom Position at bottom of viewport
.dialog-scrollable Enable body scrolling (max-height: 50vh)
.dialog-divider Add dividers to header and footer
.dialog-footer-start Left-align footer actions
.dialog-footer-center Center-align footer actions
.dialog-footer-between Space-between footer actions

Invoker Commands

Use these attributes on <button> elements. Give each <dialog> a unique id (prefix docs demos with demo-).

Attribute Description
commandfor="id" ID of the <dialog> to control
command="show-modal" Open the dialog as a modal
command="close" Close the dialog

Native Dialog Methods

Use these for programmatic control. Prefer Invoker Commands on buttons.

Method Description
showModal() Open dialog as modal with backdrop
show() Open dialog without backdrop
close() Close the dialog

Native Dialog Properties

Property Description
open Boolean indicating if dialog is open
returnValue String value passed to close()
  • Button - Dialog action buttons
  • Modal - Legacy CSS visibility API retained for compatibility
  • Actions overview - Choosing among action primitives
  • Card - Similar container component
  • Alert - For inline notifications

See Also