드롭다운 메뉴 하나 만드는데 매번 같은 코드를 반복했다. 바깥 클릭 감지하는 useEffect, Esc 눌렀을 때 닫는 핸들러, 그리고 잘림 방지를 위한 z-index: 9999. 부모에 overflow: hidden이 걸려 있으면 메뉴가 잘리니까 결국 position: fixed로 도망가거나 포탈로 body에 붙이고. 이 뻔한 걸 왜 매번 손으로 만들어야 하나 싶었다.
Popover API는 이걸 브라우저가 대신 해준다. 이제 크롬, 사파리, 파이어폭스 다 지원한다.
기본 사용법
popover 속성을 요소에 붙이고, 여는 버튼에 popovertarget으로 id를 연결하면 끝이다.
<button popovertarget="menu">메뉴 열기</button>
<div id="menu" popover>
<a href="/profile">프로필</a>
<a href="/settings">설정</a>
<a href="/logout">로그아웃</a>
</div>
JS를 한 줄도 안 썼는데 버튼을 누르면 열리고, 다시 누르거나 바깥을 클릭하거나 Esc를 누르면 닫힌다. 이 바깥 클릭·Esc로 닫히는 동작을 **라이트 디스미스(light dismiss)**라고 하는데, 그동안 직접 만들던 게 기본으로 딸려온다.
top layer라서 안 잘린다
가장 큰 이점은 popover 요소가 top layer에 렌더링된다는 점이다. DOM상 어디에 있든, 부모에 overflow: hidden이나 transform이 걸려 있든 상관없이 항상 최상단에 뜬다. z-index를 올릴 필요가 없고 포탈도 필요 없다. z-index 값 비교하다 지치던 그 지옥이 사라진다.
두 가지 모드
popover 값에는 두 종류가 있다.
popover="auto"(기본값) — 라이트 디스미스 됨. 동시에 하나만 열림. 드롭다운, 메뉴에 적합.popover="manual"— 바깥 클릭으로 안 닫힘. 코드로만 닫아야 함. 토스트 알림처럼 여러 개 떠 있어야 할 때.
버튼 하나로 열고 닫기 말고 세밀하게 제어하고 싶으면 JS API도 있다.
const menu = document.getElementById('menu');
menu.showPopover();
menu.hidePopover();
menu.togglePopover();
menu.addEventListener('toggle', (e) => {
if (e.newState === 'open') console.log('열림');
});
스타일링
열린 상태는 :popover-open 의사 클래스로, 뒷배경은 ::backdrop으로 잡는다.
[popover] {
border: 1px solid #ddd;
border-radius: 8px;
padding: 8px;
}
[popover]:popover-open {
animation: fade-in 0.15s ease-out;
}
한 줄 정리
<dialog>가 화면을 잠그는 모달용이라면, popover는 잠그지 않고 살짝 띄우는 메뉴·툴팁·팝오버용이다. 위치까지 앵커에 딱 붙이고 싶으면 CSS Anchor Positioning과 조합하면 된다. 드롭다운 하나 만들자고 라이브러리 깔던 습관을 이제 좀 버려도 될 것 같다.