Home / Snippets / UI Components /

Dialog modal

Native <dialog> element — accessible, keyboard-friendly, styleable.

Confirm action

Are you sure you want to proceed? This action cannot be undone.

Widely Supported
ui

Quick implementation

/* HTML: <dialog class="modal">...</dialog> */
/* JS: dialog.showModal() to open, dialog.close() to close */

.modal {
  border: 1px solid oklch(0.30 0.02 260);
  border-radius: 1rem;
  background: oklch(0.17 0.02 260);
  color: oklch(0.93 0.01 260);
  padding: 2rem;
  max-width: 24rem;
  width: min(90%, 24rem);
}

.modal::backdrop {
  background: oklch(0 0 0 / 0.6);
  backdrop-filter: blur(4px);
}

/* Open animation */
.modal[open] {
  animation: modal-in 0.2s ease-out;
}

@keyframes modal-in {
  from {
    opacity: 0;
    transform: scale(0.95) translateY(-0.5rem);
  }
}

Prompt this to your LLM

Includes role, constraints, two framework variants, and edge cases to handle.

You are a senior frontend engineer building a modal dialog component.

Goal: A dialog modal using the native <dialog> element with styled backdrop and open animation.

Technical constraints:
- Use the native <dialog> element — not a div with role="dialog".
- Open with dialog.showModal(), close with dialog.close().
- Style ::backdrop with semi-transparent overlay and optional backdrop-filter: blur.
- Use oklch() for all colors, not hex or rgba().
- Open animation: scale + translateY with opacity fade-in.
- Max width: 24rem with width: min(90%, 24rem) for mobile.

Framework variant (pick one):
A) Vanilla HTML + CSS + minimal JS (showModal/close).
B) React component — accept isOpen, onClose, title, children props.

Edge cases to handle:
- Escape key closes the dialog (native behavior — don't break it).
- Focus trap is handled by the browser with showModal() — do not add custom focus trap.
- Scrolling the body should be prevented while the dialog is open.
- Respect prefers-reduced-motion for the open animation.

Return HTML + CSS + minimal JS.

Why this matters in 2026

The <dialog> element provides focus trapping, Escape key handling, and top-layer rendering for free. Before it shipped, every modal required hundreds of lines of JavaScript for accessibility. Now the browser handles the hard parts — your job is just CSS styling.

The logic

dialog.showModal() places the dialog in the top layer, above all other content, and renders the ::backdrop pseudo-element behind it. The dialog is automatically centered and trapped for focus. The ::backdrop styles add a dark overlay with optional blur. The [open] attribute selector triggers the entrance animation.

Accessibility & performance

The native <dialog> element provides built-in focus trapping — when opened with showModal(), Tab cycles only within the dialog. Escape closes it. Screen readers announce it as a dialog. Do not add role="dialog" — it's already implied. The backdrop-filter: blur() uses GPU compositing but can be expensive on large pages; test on low-end devices.