<dialog> Element

<dialog> 엘리먼트

The native modal element — focus trapping, Esc-to-close, top-layer rendering, and `::backdrop` all handled by the browser.

Also known as: Native modalshowModal()
···
html
<div class="wrap">
  <div class="badge" id="badge"><svg viewBox="0 0 10 10"><path id="bpath" d="M1.5 5.2l2.6 2.6L8.5 2.4" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg><span id="btext">확인 중</span></div>
  <button id="btn" class="trigger">모달 열기</button>
  <dialog id="dlg" class="dlg">
    <strong>정말 삭제할까요?</strong>
    <p>되돌릴 수 없어요.</p>
    <button id="close" class="mini">닫기</button>
  </dialog>
</div>
css
.wrap{position:relative;width:100%;height:100%;display:grid;place-items:center}
.trigger{padding:9px 18px;border-radius:9px;border:1px solid var(--line);background:var(--surface);color:var(--fg);font-size:13px;font-weight:600}
.dlg{border:1px solid var(--line);border-radius:12px;padding:16px;background:var(--surface);color:var(--fg);width:200px}
.dlg::backdrop{background:rgba(10,10,16,.45)}
.dlg p{margin:4px 0 10px;font-size:11px;color:var(--muted)}
.mini{font-size:11px;font-weight:600;border:1px solid var(--line);background:none;color:var(--fg);border-radius:6px;padding:5px 10px}
.dlg.fallback-open{display:block;position:fixed;inset:0;margin:auto;z-index:50}
.dlg.fallback-open::before{content:"";position:fixed;inset:0;background:rgba(10,10,16,.45);z-index:-1}
.badge{position:absolute;top:10px;right:10px;display:flex;align-items:center;gap:5px;padding:4px 9px;border-radius:999px;font-size:10px;font-weight:700;background:var(--bg);border:1px solid var(--line);color:var(--accent-3);z-index:2}
.badge.no{color:var(--accent-2)}
.badge svg{width:9px;height:9px}
js
const ok = typeof HTMLDialogElement !== 'undefined' && 'showModal' in document.createElement('dialog');
const b=document.getElementById('badge'),p=document.getElementById('bpath'),t=document.getElementById('btext');
b.classList.toggle('no', !ok);
p.setAttribute('d', ok ? 'M1.5 5.2l2.6 2.6L8.5 2.4' : 'M2 2l6 6M8 2l-6 6');
t.textContent = ok ? '<dialog> 지원됨' : '미지원 · 커스텀 모달 폴백';
const dlg = document.getElementById('dlg'), btn = document.getElementById('btn'), close = document.getElementById('close');
function open(){ ok ? dlg.showModal() : dlg.classList.add('fallback-open'); }
function shut(){ ok ? dlg.close() : dlg.classList.remove('fallback-open'); }
btn.addEventListener('click', open); close.addEventListener('click', shut);
open();
setInterval(() => (ok ? dlg.open : dlg.classList.contains('fallback-open')) ? shut() : open(), 2200);

A hand-built modal has a lot to get right — trapping Tab inside it, closing on Esc, dimming the background, and wiring `role="dialog"` and `aria-modal` correctly for screen readers. Miss any one of these and it's an accessibility bug.

`<dialog>` solves this at the element level. Calling `.showModal()` renders it in the top layer, traps focus automatically, closes on Esc, and exposes a `::backdrop` pseudo-element for styling the scrim. Worth knowing: `.show()` (modeless) differs from `.showModal()` (modal), and a form with `method="dialog"` closes the dialog automatically on submit.

Check caniuse/MDN baseline for support. Without it, a custom modal with hand-rolled focus trapping, an Esc listener, and a backdrop div is the fallback — and still used today when animation needs go beyond what `<dialog>` offers.

When to use

Building a screen-blocking confirmation or settings modal quickly, without accessibility bugs.