Popover API

팝오버 API

The native overlay API — one HTML `popover` attribute gives you top-layer rendering, light-dismiss, and Esc-to-close for free.

Also known as: popover attributepopovertarget
···
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" popovertarget="pop">알림 열기</button>
  <div id="pop" popover class="pop">
    <strong>새 메시지 3개</strong>
    <p>바깥을 누르거나 Esc를 누르면 닫혀요.</p>
  </div>
</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}
.pop{position:fixed;inset:auto auto 20% 50%;translate:-50% 0;margin:0;border:1px solid var(--line);border-radius:12px;padding:14px 16px;
  background:var(--surface);color:var(--fg);width:200px;box-shadow:0 16px 34px rgba(0,0,0,.22)}
.pop p{margin:4px 0 0;font-size:11px;color:var(--muted)}
.pop.fallback-open{display:block}
.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 = 'popover' in HTMLElement.prototype;
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 ? 'popover 지원됨' : '미지원 · JS 폴백';
const pop = document.getElementById('pop'), btn = document.getElementById('btn');
if (!ok) {
  // 폴백: position:fixed + 수동 open/close + 바깥 클릭 감지
  pop.style.display = 'none';
  pop.removeAttribute('popover'); btn.removeAttribute('popovertarget');
  function open(){ pop.style.display = 'block'; }
  function close(){ pop.style.display = 'none'; }
  btn.addEventListener('click', (e) => { e.stopPropagation(); pop.style.display === 'block' ? close() : open(); });
  document.addEventListener('click', (e) => { if (!pop.contains(e.target)) close(); });
  window.__demoToggle = () => (pop.style.display === 'block' ? close() : open());
} else {
  window.__demoToggle = () => pop.togglePopover();
}
window.__demoToggle();
setInterval(() => window.__demoToggle(), 2200);

The most annoying part of building an overlay (dropdown, tooltip, notification) has always been the z-index war and hand-rolling "click outside to close." No z-index value was safe if an ancestor used `overflow:hidden` or `transform` — it would still get clipped — and light-dismiss meant attaching and detaching a document click listener yourself.

Add the `popover` attribute to an element and it renders in the top layer — above the viewport, above every other stacking context. Give the trigger button `popovertarget="id"` and clicking opens/closes it, with outside-click and Esc handled by the browser. To control it from JS, call `el.showPopover()` / `hidePopover()` / `togglePopover()`.

Check caniuse/MDN baseline for support. Without it, you're back to `position:fixed` plus manual z-index and a document click listener — the fallback path in the demo below is the minimal version of that.

When to use

Dropdowns, notifications, simple dialogs — anything that needs to float above everything else and close on outside click.