Actually using html/css popovers
Ok here’s the hello world everyone puts on their “did you know HTML has popovers?” page.
<button popovertarget=foo>I'm button</button>
<div id=foo popover>I'm popover</div>Wow, native no-JS popovers, ooh, ahh. Now let’s try to actually use them.
Backdrop
There’s no separation between the popover and the page content so it feels weird. We can style the backdrop with ::backdrop to give it a nice transparent background.
<button popovertarget=bar>Show popover</button>
<div id=bar popover>The background is pretty cool</div>
<style>
[popover]::backdrop {
background: rgba(0,0,0,0.25);
}
</style>The problem is that this type of background feels like it should block clicks, but doesn’t. Clicking outside the popover dismisses it but also clicks on any links underneath the cursor. This might immediately open another popover which makes it feel like the click didn’t even dismiss the popover. It’s just a bad experience. (Here’s a link to play with.)
Blocking clicks through the backdrop
No, pointer-events on the ::backdrop doesn’t work, don’t try it. Instead you need to make your own div which blocks clicks and show it when there’s an open popover.
<button popovertarget=baz>Show this popover</button>
<div id=baz popover>The background is fake</div>
<div class=fakebackdrop></div>
<style>
.fakebackdrop {
display: none;
}
:popover-open + .fakebackdrop {
display: block;
position: fixed;
z-index: 10;
inset: 0;
background: rgba(0,0,0,0.25);
pointer-events: auto;
}
</style>Bleh.
Wait, also blocking keyboard focus
Open a popover, hit tab/shift-tab a few times, and you’re still able to focus on things underneath the popover.
We’re looking for something called a “focus trap”. Now’s probably a good time to mention the difference between popovers and dialogs and note that dialogs come with focus trapping behavior. Great!
<button popovertarget=quux>Show the dialog popover</button>
<dialog id=quux popover>I'm a dialog</dialog>Oh wait that didn’t work at all. Let’s have another look at that css-tricks article:
That said, I don’t recommend using the Popover API for modality because it doesn’t have a
showModal()method (that<dialog>has) that creates inertness, focus trapping, and other necessary features to make it a real modal.
So my understanding is that just because the popover is a dialog does not mean it gets dialog behavior. You can open a dialog with the popover api and make it act like a popover, or you can open it with the dialog API and it will act like a dialog? I suppose?
Real modals
For a while browsers had the popover API, controllable without javascript, and separately also a dialog API but no way to use that without javascript. Brand spanking new in 2025 is the invoker commands api. Together at last?
<button commandfor=asdf command=show-modal>Show the dialog</button>
<dialog id=asdf>I'm no longer a popover, just a dialog</dialog>This traps focus as expected, pretty good, aaaaaand now you can’t close it by clicking outside anymore
Closedby
You goof, you buffoon, you forgot to set closedby=any on the dialog. Popovers show a popover with light dismiss behavior, showModal shows a modal but not one with light dismiss behavior, clicking outside is called light dismiss? I guess.
<button commandfor=sdfg command=show-modal>Show the closedby=any dialog</button>
<dialog id=sdfg closedby=any>Finally a decent modal</dialog>Finally a decent modal. Also it comes with a little backdrp as part of the default stylesheet, and that backdrop actually blocks clicks! I’m astonished.
Ok I should admit an xy problem
I want this specific type of modal because I’m making a photo gallery/lightbox system, and wanted to get as far as I could using html popovers/dialogs. So let’s add an image thumbnail inside the <button> and an image inside the popover.
Well because it’s still a <button> the browser adds native styling to the thumbnail and it looks like trash. But it has to be a <button> because the invoker commands API does not work on literally any element other than buttons, for… some reason. So okay fine it’ll be a button.
debuttoning the button
I think the easiest way to do this is to style the button with all: unset; and then deal with the fallout of that (like missing focus styles and lack of an indication that it can be clicked)
Recap
What do I want.
- Some kind of dialog/popup/modal/whatever, which displays an overlay on top of the page
- Keyboard focus stays within the modal and doesn’t wander all over the rest of the page
- When you press ESC it closes
- When you click on the background it also closes, and, importantly, does NOT click on anything else
Does any of this even matter
The only people who disable javascript are paranoid computer nerds who run uMatrix on everything. (As we speak, said nerds are getting very mad at me and composing a sternly-worded email, and won’t be able to read my response because it’d be an HTML email)
What I’m saying is I think I could have saved myself a bit of headache by pulling in a boring, well-tested javascript popover/modal/dialog/lightbox/whatever library that people have been using for ten years. It will work because everyone who matters runs Javascript, and I can assuage other progressive-enhancement fears like “what if the javascript is too big or it doesn’t load” by making the javascript not too big, and ensuring it loads. (What if the stylesheet doesn’t load? What if the HTML doesn’t load?)
Shrug.