Introducing the popover API
Popovers are everywhere on the web. You can see them in menus, toggletips, and dialogs, which could manifest as account settings, disclosure widgets, and product card previews. Despite how prevalent these components are, building them in browsers is still surprisingly cumbersome. You need to add scripting to manage focus, open and close states, accessible hooks into the components, keyboard bindings to enter and exit the experience, and that’s all even before you start building the useful, unique, core functionality of your popover.
To resolve this, a new set of declarative HTML APIs for building popovers is coming to browsers, starting with the
popover API in Chromium 114.
The popover attribute
- Chrome 114, Supported 114
- Firefox 114, Behind a flag
- Edge 114, Supported 114
- Safari preview, Preview
Rather than managing all of the complexity yourself, you can let the browser handle it with the
popover attribute and subsequent set of features. HTML popovers support:
- Promotion to the top layer. Popovers will appear on a separate layer above the rest of the page, so you don’t have to futz around with z-index.
- Light-dismiss functionality. Clicking outside of the popover area will close the popover and return focus.
- Default focus management. Opening the popover makes the next tab stop inside the popover.
- Accessible keyboard bindings. Hitting the
esckey will close the popover and return focus.
- Accessible component bindings. Connecting a popover element to a popover trigger semantically.
popoverattribute on the element containing the popover.
idon the element containing the popover.
popovertargetwith the value of the popover's
idon the element that opens the popover.
<button popovertarget="my-popover"> Open Popover </button>
<div id="my-popover" popover>
<p>I am a popover with more information.<p>
Now you have a fully-functional basic popover.
This popover could be used to convey additional information or as a disclosure widget.
Defaults and overrides
By default, such as in the previous code snippet, setting up a popover with a
popovertarget means the button or element that opens the popover will toggle it open and closed. However, you can also create explicit popovers using
popovertargetaction. This overrides the default toggle action.
popovertargetaction options include:
popovertargetaction="show": Shows the popover.
popovertargetaction="hide": Hides the popover.
popovertargetaction="hide", you can create a “close” button within a popover, as in the following snippet:
<button popovertarget="my-popover" popovertargetaction="hide">
Auto versus manual popovers
popover attribute on its own is actually a shortcut for
popover="auto". When opened, the default
popover will force close other auto popovers, except for ancestor popovers. It can be dismissed via light-dismiss or a close button.
On the other hand, setting
popover=manual creates another type of popover: a manual popover. These do not force close any other element type and do not close via light-dismiss. You must close them via a timer or explicit close action. Types of popovers appropriate for
popover=manual are elements which appear and disappear, but shouldn't affect the rest of the page, such as a toast notification.
If you explore the demo above, you can see that clicking outside of the popover area doesn't light-dismiss the popover. Additionally, if there were other popovers open, they wouldn't close.
To review the differences:
- When opened, force-close other popovers.
- Can light-dismiss.
- Do not force close any other element type.
- Do not light-dismiss. Close them using a toggle or close action.
So far you've learned about basic popovers in HTML. But there are also some nice styling features that come with
popover. One of those is the ability to style
auto popovers, this is a layer directly beneath the top layer (where the popover lives), which sits above the rest of the page. In the following example, the
::backdrop is given a semi-transparent color:
background: rgb(190 190 190 / 50%);
By default, popovers get a 2px border and are positioned in the center of the UI, but they are fully customizable! You can style a popover just like any other HTML element: you can change its size, background, position on the page, and so on.
The difference between a
popover and a
It's important to note that the
popover attribute does not provide semantics on its own. And while you can now build modal dialog-like experiences using
popover=”auto”, there are a few key differences between the two:
dialog element opened with
dialog.showModal (a modal dialog), is an experience which requires explicit user interaction to close the modal. A
popover supports light-dismiss. A modal
dialog does not. A modal dialog makes the rest of the page inert. A
popover does not.
The above demo is a semantic dialog with popover behavior. This means that the rest of the page is not inert and that the dialog popover does get light-dismiss behavior. You can build this dialog with popover behavior using the following code:
<dialog popover id="candle-01">
<button class="close-btn" popovertarget="candle-01" popovertargetaction="hide">...</button>
The WhatWG and OpenUI community group are currently discussing the ability to open a dialog element with HTML ergonomics. This would be similar to popover, but retain the dialog features listed previously, such as making the rest of the page inert. Watch these groups for the future of
dialog, and new elements like
Interactive entry and exit
The ability to animate discrete properties, including animating to and from
display: none and animating to and from the top layer are not yet available in browsers. However, they are planned for an upcoming version of Chromium, closely following this release.
With the ability to animate discrete properties, and using
@starting-style, you'll be able to set up before-change and after-change styles to enable smooth transitions when opening and closing popovers. Take the previous example. Animating it in and out looks much smoother and supports a more fluid user experience:
The implementation for this is currently in flux, but click through to the codepen demo for the latest syntax and try it out with the #experimental-web-platform-features flag turned on in Chrome Canary.
Popovers are great when you want to position an alert, modal, or notification based on the viewport. But popovers are also useful for menus, tooltips, and other elements that need to be positioned relative to other elements. This is where CSS anchoring comes in.
The following radial menu demo uses the popover API along with CSS anchor positioning to ensure that the popover
#menu-items is always anchored to its toggle trigger, the
Setting up anchors is similar to setting up popovers:
<button id="menu-toggle" popovertarget="menu-items">
<ul id="menu-items" popover anchor="menu-toggle">
You set up an anchor by giving it an
id (in this example,
#menu-toggle), and then use
anchor="menu-toggle" to connect the two elements. Now, you can use
anchor() to style the popover. A centered popover menu that is anchored to the baseline of the anchor toggle might be styled as follows:
translate: -50% 0;
There are even more exciting new features of CSS anchoring, such as
@try statements to swap the position of the menu based on its available viewport space. This implementation is subject to chance. Explore the Codepen demo above with the #experimental-web-platform-features flag turned on in Chrome Canary for more.
The popover API is the first step in a series of new capabilities to make building web applications easier to manage and more accessible by default. I'm excited to see how you use popovers!