화면 위에 떠도 modal은 아닙니다
계정 menu를 popover로 열면 top layer에 표시되고 Esc나 바깥쪽 click으로 닫을 수 있습니다. 이 모습 때문에 삭제 확인 창도 같은 방식으로 만들기 쉽지만, 중요한 결정을 내리는 동안 배경 조작을 막아야 한다면 동작 계약이 맞지 않습니다. popover와 modal dialog 경계는 화면 모양이 아니라 나머지 page를 계속 사용할 수 있어야 하는지로 결정합니다.
Popover API로 연 element는 항상 non-modal이라 page의 나머지 영역을 inert로 만들지 않으며, 배경 상호작용을 막아야 하는 작업에는 dialog.showModal()이 맞습니다.
action menu에는 popover를 사용하는 흐름
popover 또는 popover="auto"는 기본적으로 light dismiss를 지원하고, 보통 다른 auto popover가 열리면 기존 것을 닫습니다. trigger에는 실제 button과 popovertarget을 사용합니다. 이렇게 연결하면 browser가 펼침 상태와 focus navigation 관계를 보조하지만, popover 내용 자체의 의미까지 자동으로 정해 주지는 않습니다. 이 예제는 link 모음이므로 nav와 접근 가능한 이름을 명시합니다.
<button type="button" popovertarget="account-menu">
계정 메뉴
</button>
<nav id="account-menu" popover="auto" aria-label="계정 메뉴">
<a href="/profile">프로필</a>
<a href="/settings">설정</a>
</nav>
trigger와 popover를 DOM에서 가깝게 두면 assistive technology의 reading order도 이해하기 쉽습니다. popover="manual"은 자동 닫힘을 끌 뿐 modal로 바꾸지는 않습니다.
배경을 막아야 하면 dialog를 선택합니다
삭제 확인이나 필수 동의처럼 응답 전까지 page의 다른 control을 사용하면 안 되는 작업은 dialog.showModal()을 사용합니다. modal dialog는 바깥 문서를 inert로 만들지만 focus 이동, 명확한 제목, 닫기 control과 실행 뒤 focus 복귀는 실제 keyboard와 screen reader로 확인해야 합니다.
<button type="button" id="open-delete">계정 삭제</button>
<dialog id="delete-dialog" aria-labelledby="delete-title">
<h2 id="delete-title">계정을 삭제할까요?</h2>
<p>삭제하면 저장한 학습 기록도 함께 사라집니다.</p>
<form method="dialog">
<button value="cancel">취소</button>
<button value="confirm">삭제</button>
</form>
</dialog>
<script>
const dialog = document.querySelector('#delete-dialog');
document.querySelector('#open-delete').addEventListener('click', () => {
dialog.showModal();
});
</script>
완료 기준은 mouse 모양이 아니라 Tab 순서, Esc와 바깥 click 정책, 배경 control 차단, 닫힌 뒤 trigger로의 focus 복귀입니다. target browser의 Popover API 지원 범위도 함께 확인합니다.
공식 문서
Popover API와 dialog의 현재 browser 지원 및 제품의 dismissal 정책을 함께 검증합니다.