Loading Developer Playground

Loading ...

Skip to main content
ARIA ROLEWindow Roles

dialog

A dialog is a window overlaid on either the primary window or another dialog window. Dialogs can be modal (require interaction before returning) or non-modal.

Implicit Role
None
Required Attributes
aria-labelledby or aria-label
Keyboard Support
Tab, Escape

Overview

The dialog role is used to mark up an HTML element as a dialog that temporarily blocks interaction with the main content.

Dialogs are typically used to prompt the user for information, provide a warning message, or require the user to make a decision. When a dialog is active, all other content on the page should be inert (not interactive).

Modal vs Non-Modal

A modal dialog requires user interaction before returning to the main content (use aria-modal="true"). A non-modal dialog allows users to interact with content outside the dialog.

Live Demo

Click the button to see an accessible dialog in action

Required Attributes

aria-labelledbyoraria-label

Every dialog must have an accessible name. Use aria-labelledby to reference the dialog's title element, or aria-label to provide a direct label.

Supported Attributes

aria-modal

Indicates the dialog is modal (true) or non-modal (false)

aria-describedby

References element(s) that describe the dialog's content

aria-labelledby

References the element that labels the dialog (usually the title)

aria-label

Provides a string label for the dialog

Code Examples

Basic Dialog

<!-- Basic Dialog Example -->
<div role="dialog" 
     aria-labelledby="dialog-title" 
     aria-describedby="dialog-desc"
     aria-modal="true">
  <h2 id="dialog-title">Delete File</h2>
  <p id="dialog-desc">
    Are you sure you want to delete this file? This action cannot be undone.
  </p>
  <div>
    <button>Cancel</button>
    <button>Delete</button>
  </div>
</div>

Dialog with Backdrop

<!-- Dialog with Backdrop -->
<div class="dialog-backdrop" aria-hidden="true">
  <div role="dialog" 
       aria-labelledby="dialog-title"
       aria-modal="true">
    <button aria-label="Close dialog">×</button>
    <h2 id="dialog-title">Settings</h2>
    <form>
      <label for="username">Username</label>
      <input type="text" id="username" />
      <button type="submit">Save</button>
    </form>
  </div>
</div>

Focus Management

// JavaScript Focus Management
function openDialog(dialogId) {
  const dialog = document.getElementById(dialogId);
  const focusableElements = dialog.querySelectorAll(
    'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
  );
  
  // Store previously focused element
  const previouslyFocused = document.activeElement;
  
  // Show dialog
  dialog.hidden = false;
  dialog.setAttribute('aria-modal', 'true');
  
  // Focus first element
  focusableElements[0].focus();
  
  // Trap focus within dialog
  dialog.addEventListener('keydown', (e) => {
    if (e.key === 'Tab') {
      trapFocus(e, focusableElements);
    } else if (e.key === 'Escape') {
      closeDialog(dialog, previouslyFocused);
    }
  });
}

function trapFocus(e, focusableElements) {
  const firstElement = focusableElements[0];
  const lastElement = focusableElements[focusableElements.length - 1];
  
  if (e.shiftKey && document.activeElement === firstElement) {
    e.preventDefault();
    lastElement.focus();
  } else if (!e.shiftKey && document.activeElement === lastElement) {
    e.preventDefault();
    firstElement.focus();
  }
}

React Component

// React Dialog Component
import { useEffect, useRef } from 'react';

function Dialog({ isOpen, onClose, title, children }) {
  const dialogRef = useRef(null);
  const previousFocusRef = useRef(null);
  
  useEffect(() => {
    if (isOpen) {
      // Store previous focus
      previousFocusRef.current = document.activeElement;
      
      // Focus first focusable element in dialog
      const focusableElements = dialogRef.current.querySelectorAll(
        'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
      );
      if (focusableElements.length > 0) {
        focusableElements[0].focus();
      }
      
      // Prevent body scroll
      document.body.style.overflow = 'hidden';
    } else {
      // Restore focus
      if (previousFocusRef.current) {
        previousFocusRef.current.focus();
      }
      
      // Restore body scroll
      document.body.style.overflow = '';
    }
    
    return () => {
      document.body.style.overflow = '';
    };
  }, [isOpen]);
  
  if (!isOpen) return null;
  
  return (
    <div
      className="dialog-backdrop"
      onClick={onClose}
      aria-hidden="true"
    >
      <div
        ref={dialogRef}
        role="dialog"
        aria-labelledby="dialog-title"
        aria-modal="true"
        onClick={(e) => e.stopPropagation()}
      >
        <h2 id="dialog-title">{title}</h2>
        {children}
        <button onClick={onClose}>Close</button>
      </div>
    </div>
  );
}

Keyboard Support

Tab

Moves focus to the next focusable element inside the dialog. If focus is on the last element, moves to the first.

Shift + Tab

Moves focus to the previous focusable element inside the dialog. If focus is on the first element, moves to the last.

Escape

Closes the dialog and returns focus to the element that triggered it.

Best Practices

Always provide an accessible name using aria-labelledby or aria-label

Focus must be moved to an element inside the dialog when it opens

Focus must be trapped within the dialog (Tab cycles through focusable elements)

When the dialog closes, focus must return to the element that triggered it

The Escape key should close the dialog

For modal dialogs, use aria-modal=&quot;true&quot; and make background content inert

Use aria-describedby to reference the main content description

×

Don't use role="dialog" without proper focus management

×

Don't allow focus to escape the dialog when it's modal

×

Don't forget to disable scrolling on the body when modal is open

Accessibility Notes

Screen Reader Support

When a dialog opens, screen readers will announce the dialog's label and role. Users can navigate through the dialog's content using standard navigation keys. The aria-modal attribute informs assistive technologies that content outside the dialog is inert.

Focus Trap

Focus trapping is critical for modal dialogs. Without it, keyboard and screen reader users can navigate to background content that should be inert. Use JavaScript to trap focus within the dialog and return it to the triggering element when closed.

Initial Focus

When the dialog opens, focus should be placed on the first focusable element, unless there's a more appropriate element (like a confirmation button in a warning dialog). Avoid focusing on the close button as the first element.

Related Roles

Specifications & Resources