Dialog and Popover
Two new primitives changed the modal story in the last few years.
<dialog>
Section titled “<dialog>”Native, modal, focus-trapping, escape-closing, backdrop-rendering, with a real browser-managed inert state for the rest of the page.
<button id="open-confirm">Delete account</button>
<dialog id="confirm"> <form method="dialog"> <p>Are you sure? This cannot be undone.</p> <menu> <button value="cancel">Cancel</button> <button value="confirm">Yes, delete</button> </menu> </form></dialog>
<script> const dialog = document.getElementById('confirm') document.getElementById('open-confirm').onclick = () => dialog.showModal() dialog.addEventListener('close', () => { if (dialog.returnValue === 'confirm') { // do the deletion } })</script>What you didn’t have to write:
- A focus trap (the browser implements it).
- An
Escapehandler (the browser implements it). - A backdrop (the browser renders it; style with
::backdrop). - Inerting the rest of the page (the browser does it).
- A return value mechanism (
form method="dialog"provides it). - Screen reader announcement of dialog open (the browser handles it).
The dialog is a real <dialog>. No portals. No aria-modal. No tabindex juggling. The browser knows it’s a dialog because it is a dialog.
<kit-dialog> is a thin wrapper that adds default styling, declarative open/close, and a built-in dialogModule that handles dialog.open and dialog.close commands by id — so any element with meta-command="dialog.open" meta-prop-target="my-dialog" opens it without writing JS.
popover
Section titled “popover”For non-modal floating UI — menus, tooltips, dropdowns, command palettes — the popover attribute is what <dialog> is for modals.
<button popovertarget="settings-menu">Settings</button>
<div id="settings-menu" popover> <button>Profile</button> <button>Notifications</button> <button>Sign out</button></div>That works. No JavaScript. The browser handles open, close, light-dismiss (clicking outside), focus management, and the top-layer rendering that keeps the popover above clipped scroll containers.
popover="manual" for popovers that don’t auto-close. popovertargetaction="show|hide|toggle" for explicit control. :popover-open for state-based styling. popover-show / popover-hide events for behavior.
Why this matters for Kitsune
Section titled “Why this matters for Kitsune”Most frontend “kits” ship a <Popover> or <Dropdown> that re-implements these behaviors in JavaScript, often poorly, often inconsistently across browsers. Kitsune doesn’t. We use the platform.
The Quill tutorial’s command palette (chapter 9) is a <div popover> with native search filtering. The confirm dialog (chapter 7) is <kit-dialog> wrapping <dialog>. The toast region uses aria-live="polite" and renders below the document so screen readers announce changes.
The pattern across all three: use the native primitive, decorate it through CSS and metadata, let the browser handle the hard parts.
A note on browser support
Section titled “A note on browser support”<dialog> is in every modern browser as of 2022.
popover reached every modern browser by 2024.
If your audience includes browsers older than that, both have well-understood polyfills. For most apps in 2026, neither is necessary.