<dialog> 엘리먼트

<dialog> Element

포커스 트랩·Esc 닫기·top layer 렌더링·`::backdrop`을 전부 브라우저가 대신해주는 네이티브 모달 엘리먼트.

다른 이름: 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);

직접 만든 모달은 신경 쓸 게 많습니다 — Tab이 모달 밖으로 새지 않게 포커스를 가두고, Esc로 닫히게 하고, 배경을 어둡게 깔고, 스크린리더에 `role="dialog"`와 `aria-modal`을 정확히 달아야 합니다. 이 중 하나만 빠져도 접근성 결함이 됩니다.

`<dialog>`는 이걸 엘리먼트 자체가 해결합니다. `.showModal()`을 호출하면 top layer에 렌더링되고, 포커스가 자동으로 갇히고, Esc로 닫히며, `::backdrop` 가상 요소로 배경을 스타일링할 수 있습니다. `.show()`(모달리스)와 `.showModal()`(모달)은 서로 다르다는 점, 폼의 `method="dialog"`로 제출 시 자동으로 닫힌다는 점이 자주 쓰이는 디테일입니다.

기본 지원은 caniuse/MDN baseline을 확인하세요. 미지원 브라우저에서는 직접 포커스 트랩·Esc 리스너·backdrop div를 구현한 커스텀 모달이 대안이며, 지금도 애니메이션 커스터마이징이 더 필요하면 커스텀 구현이 쓰입니다.

언제 쓰나

확인창·설정창처럼 화면을 완전히 막는 모달을 접근성 버그 없이 빠르게 만들 때.