Skip to content

Dialog Element

The <dialog> element provides a native way to create modal and non-modal dialogs with built-in accessibility features.

<div class="modal-overlay" id="overlay" onclick="closeModal()">
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title">
<button class="close-btn" onclick="closeModal()" aria-label="Close">×</button>
<h2 id="modal-title">Confirm Action</h2>
<p>Are you sure you want to proceed?</p>
<button onclick="confirm()">Yes</button>
<button onclick="closeModal()">No</button>
</div>
</div>
<style>
.modal-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
}
.modal-overlay.open { display: flex; justify-content: center; align-items: center; }
.modal { background: white; padding: 2rem; border-radius: 8px; }
</style>
<script>
let previouslyFocused;
function openModal() {
previouslyFocused = document.activeElement;
document.getElementById('overlay').classList.add('open');
document.body.style.overflow = 'hidden';
trapFocus();
}
function closeModal() {
document.getElementById('overlay').classList.remove('open');
document.body.style.overflow = '';
previouslyFocused?.focus();
}
function trapFocus() {
const modal = document.querySelector('.modal');
const focusable = modal.querySelectorAll('button, [href], input, select, textarea');
const first = focusable[0];
const last = focusable[focusable.length - 1];
first.focus();
modal.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
if (e.key === 'Escape') closeModal();
});
}
</script>

Problems:

  • Significant JavaScript for accessibility
  • Manual focus trapping required
  • Must prevent body scroll manually
  • ARIA attributes must be added manually
<dialog id="confirmDialog">
<h2>Confirm Action</h2>
<p>Are you sure you want to proceed?</p>
<form method="dialog">
<button value="no">No</button>
<button value="yes">Yes</button>
</form>
</dialog>
<button onclick="document.getElementById('confirmDialog').showModal()">
Open Dialog
</button>
<script>
const dialog = document.getElementById('confirmDialog');
dialog.addEventListener('close', () => {
console.log('User selected:', dialog.returnValue);
});
</script>

Benefits:

  • Built-in focus trapping
  • ESC key closes automatically
  • Backdrop provided by browser
  • Returns focus to trigger element
  • returnValue captures button value
MethodDescription
show()Opens as non-modal (no backdrop, page still interactive)
showModal()Opens as modal (backdrop, focus trapped)
close(returnValue)Closes dialog, optionally sets returnValue
dialog {
border: none;
border-radius: 8px;
padding: 2rem;
max-width: 400px;
}
/* Style the backdrop */
dialog::backdrop {
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(2px);
}
/* Animate open/close */
dialog[open] {
animation: fade-in 0.2s ease-out;
}
@keyframes fade-in {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}

Using method="dialog" on a form inside a dialog closes it on submit:

<dialog id="formDialog">
<form method="dialog">
<label for="name">Name</label>
<input type="text" id="name" name="name" required>
<button type="submit">Save</button>
<button type="button" onclick="this.closest('dialog').close()">Cancel</button>
</form>
</dialog>
const dialog = document.getElementById('myDialog');
// Fires when dialog closes
dialog.addEventListener('close', () => {
if (dialog.returnValue === 'confirm') {
// User confirmed
}
});
// Prevent ESC from closing (if needed)
dialog.addEventListener('cancel', (e) => {
e.preventDefault();
});

Supported in all modern browsers. See caniuse.com/dialog.

  • Automatically receives focus when opened
  • Focus is trapped within modal dialog
  • ESC key closes the dialog
  • Focus returns to triggering element on close
  • Announced as dialog to screen readers
  • Add aria-labelledby pointing to dialog heading
<dialog aria-labelledby="dialog-title">
<h2 id="dialog-title">Dialog Heading</h2>
</dialog>

Use <dialog> for:

  • Confirmation prompts
  • Form modals
  • Alert messages
  • Any overlay requiring user attention

Consider alternatives for:

  • Toast notifications (use ARIA live regions)
  • Tooltips (use title or custom positioning)
  • Dropdown menus (use <details> or Popover API)