Make your content pop with the Popover API and CSS Anchor Positioning

Menus, tooltips, date pickers, or dialogs – popover content is everywhere on the web. Despite how common these components are, there are no native HTML elements for most of them (except for dialogs). And their implementation requires quite a lot of JavaScript.

All of this will change with the new Popover API combined with CSS Anchor Positioning. They provide developers with a powerful mechanism for displaying popover content on top of other page content.

A skateboarder in mid air jump. Photo: © Zachary DeBottis / pexels.com

I've prepared two demos with examples of popover content. We'll take an in-depth look at the technical aspects as well as the accessibility of the new features.

What can we do with it?

I love the saying: Show, don't tell! That's why I've created two demos to illustrate the capabilities of the new API.

At the moment, the examples only work in Chrome Canary, the nightly build for developers. Open the browser and access the chrome://flags page to enable the experimental web platform features.

Warning: The anchor positioning feature is still highly experimental. Therefore, the demos might break if the specification changes.

Example 1: Custom Tooltip

Standard tooltips created with the title attribute are pretty bland. So, I've tried my hand at a fancy custom tooltip. Click on the info icon button next to the heading:

I did my best to make the tooltip accessible, following the Tooltip Pattern defined in the ARIA Authoring Practices Guide. Unfortunately, the desing pattern is still work in progress and there's no consensus yet. Also, the Popover API doesn't account for hover and focus yet.

Example 2: Menu with a Set of Actions

I also created a demo for an accessible menu. Each item in the list of checked out books contains a menu button that toggles a menu panel with a set of actions for each book:

My implementation follows the Menu and Menubar Pattern defined in the ARIA Authoring Practices Guide. I've used the appropriate ARIA roles and properties and also implemented (most of) the recommended keyboard interaction.

Now let's examine the different building blocks that I used to create these components.

The magical popover attribute

This HTML attribute turns any element into popover content. The HTML specification states:

All HTML elements may have the popover content attribute set. When specified, the element won't be rendered until it becomes shown, at which point it will be rendered on top of other page content.

This popover content is always non-modal, meaning that the rest of the page can be interacted with while the popover is open. If you need to display a modal popover (e.g., a confirmation dialog), you should use the <dialog> element.

How to create a popover

To create a popover, you add the popover attribute to an element and also define an id. Next, you use the popovertarget attribute with the value of the popover's id on the element that opens the popover. Here's an example:

<button popovertarget="popover-1"> Info </button> <div id="popover-1" popover> Hi, I'm a popover! </div>

That's it! You just created a popover with a few lines of HTML and zero JavaScript. Awesome!

Built-in features of the Popover API

When using the popover attribute, you'll get all of the following for free:

  • Placement in the top layer: Popover content will automatically appear on the top layer, a separate layer that sits on top of all other layers displayed in a web document. No more worrying about the DOM order or z-index.
  • Light-dismiss: Clicking outside of the popover area will close the popover and return focus to the trigger element.
  • Accessible keyboard interaction: When the popover is open, you can hit the ESC key to close the popover and return focus to the trigger element.
  • Semantic relationship: Connecting a popover element to a toggle button will also create a semantic relationship for assistive technologies. Think aria-controls and aria-expanded per default.

More options and styling

By default, when you create a popover with a popovertarget, the trigger button will toggle it open and closed. Alternatively, you can also set an explicit value for the popovertargetaction attribute:

  • "hide": The button will hide a shown popover. This can be useful if you need a close button inside of the popover.
  • "show": The button will show a hidden popover.
  • "toggle": The button will toggle a popover between showing and hidden. This is the default behavior.

Furthermore, the popover attribute is actually a shortcut for popover="auto". This standard type of popover will force close other auto popovers. It can be dismissed via light-dismiss or a close button.

With popover="manual" you can create a manual popover. These popovers don't close via light-dismiss and they do not force close any other popovers. They must be closed via an explicit close action. A possible use case for manual popovers are alert snackbars that should be confirmed by the user.

Popovers also include useful CSS features: The ::backdrop pseudo-element allows effects to be added to the page content behind the popover. You can use the :popover-open pseudo-class to style popover elements when they are showing.

CSS Anchor Positioning

If you've made it this far, you might be wondering: What about the position of the popover on the page? To position our popover content relative to other elements, we actually need another new, experimental feature: Anchor Positioning.

This feature is defined in the CSS Module CSS Anchor Positioning. Right now, the document is still a “Working Draft”, but Chrome already offers the feature behind the experimental web platform features flag. The document states:

This specification defines 'anchor positioning', where a positioned element can size and position itself relative to one or more "anchor elements" elsewhere on the page.

In my examples above, I've used anchor positioning to place the tooltip and the menu panels next to their trigger buttons. Let's find out how to set up anchors and use them in our CSS styles.

How to define an anchor element

There are two different ways to set up anchors. You can turn an HTML element into an anchor by giving it an id, and then use the anchor attribute to tether another element to your anchor. Here's an example in combination with a popover:

<button id="toggle-btn-1" popovertarget="popover-1" class="toggle-btn"> Info </button> <div id="popover-1" popover anchor="toggle-btn-1"> Hi, I'm a popover! </div>

As an alternative, you can use the CSS property anchor-name to set up an anchor. It accepts a dashed-ident value like --toggle-btn. Next, you use the position-anchor property to tether another element to your anchor.

button.toggle-btn { anchor-name: --toggle-btn; } div[popover] { position-anchor: --toggle-btn; }

Position content with the anchor() function

Alright, so far you've set up an HTML element as an anchor and tethered your popover to it. Now you can use the anchor() function to position the popover. The function has three arguments:

  • Anchor element: This value specifies how to find the anchor element. You can specify an explicit anchor name with a dashed-ident value. Or, you can omit the value to use the implicit anchor that was defined through the anchor attribute or the position-anchor CSS property.
  • Anchor side: This value refers to the position of the corresponding side of the target anchor element. For example: bottom or left.
  • Fallback (optional): A length or percentage value that serves as a fallback in case of an invalid anchor function.

Here's an example that shows how to place a tooltip below its toggle button and center the content horizontally:

[role="tooltip"][popover] { position: absolute; position-anchor: --toggle-btn; top: anchor(bottom); left: anchor(center); translate: -50% 0; }

The specification includes even more features like the anchor-scroll property and the anchor-size() function. But this blog post is already long enough, so I'll skip them for now.

Accessibility of Popovers

How accessible are popovers? As my examples above show, you can build user-friendly, accessible components with the new Popover API. But that doesn't happen by itself.

Define the appropriate semantic role

Using the popover attribute can definitely help your website's accessibility. For example, with the ability to close the popover via the ESC key. Or, through the semantic relationship between the toggle button and the popover created by the popovertarget attribute.

On the other hand, the popover attribute doesn't define any implicit role for the HTML element it is used on. This means, you'll have to think about your specific use case and choose the correct native HTML element or assign the appropriate ARIA role (like tooltip or menu) yourself.

Screen Reader Support

How do screen reader users experience the interaction with web content built with the Popover API? Due to the limited browser support for the new feature, I only tested my demos with the following scenarios:

  1. NVDA 2023.1, Windows 11, Google Chrome Chanary 117.0.5876.0
  2. TalkBack, Samsung Galaxy S20, Android 13, Google Chrome Chanary 117.0.5876.0

Both demos worked great: I was able to navigate the tooltip and menus with the keyboard while NVDA announced the semantic roles and current status of the controls.

The interaction on my smartphone was also smooth: With TalkBack activated, I navigated the pages using swipe gestures while the screen reader informed me about the content, semantic roles and current status of the controls.

This makes me really optimistic about the future of the web! I plan on doing more tests with other browsers and screen readers as soon as the Popover API has better support.

Browser Support

When can we start using the Popover API and CSS Anchor Positioning in production? Unfortunately, it's going to take some time until we have cross-browser support for them.

According to caniuse.com, the popover attribute is already supported by Chrome and Edge. The Safari 17 Technology Preview promises support for the attribute and Firefox lists it as an experimental feature (behind the flag dom.element.popover.enabled).

Cross-browser support for CSS Anchor Positioning will take even longer. As far as I know, only Chrome has implemented the experimental feature so far. Because of the status as a “First Public Working Draft”, the current specification is subject to change in the future. We'll just have to wait and see, I guess.

Conclusion

The new Popover API combined with CSS Anchor Positioning will make our lives as web developers easier and help to make the web more accessible by default.

Despite the still experimental status of the new APIs, you should play around with them and familiarize yourselves with the new attributes and properties. This way, when cross-browser support arrives, you'll be ready to rumble! 😎

Useful Resources

Update on 04/05/2024

Revised code examples and explanations to account for changed CSS Anchor Positioning specification (W3C Working Draft, 26 March 2024) and the implementation in Chrome Canary.

Posted on