Future of CSS: Popover API
Tags: css, webdev, javascript, ux
Published: 2023-05-23 20:22:23

In this post we're going to be looking at not one but two new exciting things coming to CSS, the first is the [Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API). While I think it's great by itself the things we can do with it when paired with [Anchor positioning](https://developer.chrome.com/blog/tether-elements-to-each-other-with-css-anchor-positioning/) are very very cool. Both of these features are, at the time of writing, are only available in non-stable browsers.

## What is the Popover API?
The Popover API is a powerful tool for developers that offers a consistent and flexible way to display popover content on web pages. Whether you prefer declarative HTML attributes or JavaScript control, this API has got you covered.

On the web, it's quite common to present content on top of other content, on the [top-layer](https://developer.mozilla.org/en-US/docs/Glossary/Top_layer) if you will, to draw the user's attention to important information or actions. You might have encountered various terms for this, such as popups or dialog.

### HTML Attributes
- **popover**
  - **auto** (default) - this means the popover can be dismissed when clicking outside or pressing esc, also other popovers will close for this new popover.
  - **manual** - when manual a popover can only be dismissed with manually (with JS or a HTML close action).
- **popovertarget**
  - **[id]** - this attribute can be put on a `button` to allow it to control a popover, the value of the attribute should be the `id` of the target.
- **popovertargetaction**
  - **toggle** (default) - pressing a `button` with a `popovertarget` will perform this action on the popover. In this instance the popover will show if hidden and hide if shown.
  - **hide** - set `popovertarget` to display none.
  - **show** - set `popovertarget` to display block.

```html
<button popovertarget="menu" popovertargetaction="toggle">Menu</button>
<nav id="menu" popover="auto">
  <ul>
    <li><a href="/items/1">Item 1</a></li>
    <li><a href="/items/1">Item 2</a></li>
    <li><a href="/items/1">Item 3</a></li>
  </ul>
</nav>
```

### CSS Pseudo Elements and Classes
- **::backdrop** - this is a pseudo-element, it fills the entire screen behind the popover, styling this can allow you change the colour or even add blur.
- **:popover-open** - this is a pseudo-class, it is applied to the popover element when it is opened, we can use this to add opening animations.

```css
#menu {
  border: 0;
  margin-right: 0;
  box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
  max-width: 100vw;
  width: 200px;
  height: 100%;
}

#menu ul {
  list-style: none;
  padding: 0;
}

#menu ul li {
  padding: 1ch;
}

#menu::backdrop {
  background-color: rgba(200, 200, 200, 0.5);
  backdrop-filter: blur(2px);
}

#menu:popover-open {
  animation: slideIn 150ms ease-in-out;
}

@keyframes slideIn {
  0% {
    translate: 100% 0 0;
  }
  100% {
    translate: 0 0 0;
  }
}
```

### JavaScript Controls and Events
I know this article is mainly about new and exciting things coming to CSS but it would be a shame to not talk about the JS too.

#### Controls
- **PopoverElement.hidePopover()** - update the popover element and move it to its hidden state.
- **PopoverElement.showPopover()** - update the popover element and move it to its shown state.
- **PopoverElement.togglePopover()** - update the popover element and toggle between its hidden and shown states.

#### Events
- **beforetoggle** - event fired before state change visually takes place.
- **toggle** - event fired after state change visually takes place.

```js
const popover = document.querySelector('#menu[popover]');

popover.addEventListener('beforetoggle', () => {
  // I triggered first before anything visually happened.
});

popover.addEventListener('toggle', () => {
  // I triggered after the toggle had happened visually.
});
```

### Demo
This is a simple demo of a dialog box, I've chosen not to include any JavaScript in this example just to show off how simplistic it can truly be.

{% codesandbox popoverapi-simple-hbd7sp %}

Here's a fallback gif just incase your browser doesn't support `popover` yet.
![Popover fallback animation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5o8y4ssmoesysohmnljk.gif)

## What is Anchor Positioning?
Anchor Positioning is not on MDN yet so this really is some experimental stuff. I'll do my best to explain what it does and how to use it.

Currently if you want an element to render physically next another element the easiest way to do this is work out where the target element is using JavaScript than set the anchored element to position absolute and set the top, right, bottom or left attributes. This method works but 'less JavaScript is more good'™ so anchor lets you store the position of an element in a CSS variable.

```css
#anchor {
  anchor-name: --test-anchor;
}

.anchored-element {
  position: absolute;
  right: anchor(--test-anchor left);
  top: anchor(--test-anchor center);
  transform: translateY(-50%);
}
```

You'll notice this new `anchor` CSS function. It takes a CSS variable and a position and then returns position, you can even use the value in a `calc` to do more complex positions.

### Demo
Here is a little demo showing creation of a tooltip again the aim was to be simplistic and just show an easy implementation. 

{% codesandbox anchor-simple-g3odbi %}

And again, here's a fallback gif as I'm sure anchor is still is a bit of a way off.
![Anchor fallback animation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5z9k2hp5u2sc1mny4j8o.gif)

## How can we use these together?
The web has been around for a long time and is fully backwards compatible, which is great, but does lead to a few issues like not being able to customise the `select` combobox component. I'm sure many of you have had that dilemma between writing a new combobox from scratch to match a specific design or find one someone else has already made but lose some of the customisation ability.

Putting together `Popover` and `Anchor Position` I think we might be able to make a nice looking combobox that we can build to look how we like without having to worry about accessibility and positioning issues. Let's see if we can do it.

## What we doing about accessibility?
There is a website made by the lovely people over at [W3C](https://www.w3.org/) that goes through all the [aria patterns](https://www.w3.org/WAI/ARIA/apg/patterns/) for various different use cases including a [combobox](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/). I'll be looking through this as well as looking at what the native `select` element does.

I know the aria patterns site has been up for a little while now but I can't stress how amazing and useful it is, I feel like it's a bit of a best kept secret but everyone should be using it.

## The Demo
So here we are, finally, the demo. I've put together the Popover API and Anchor positioning to make a combobox that we can style to our hearts content. 

I have had to use some JS in order to make sure its keyboard friendly and to make sure the text in the button updates if any of you have ideas of ways we can use less JS to do these things I'd love to hear them.

{% codesandbox combobox-popover-zhh5u2 %}

One last fallback gif, hopefully one day we can all look back on this post and see everything just working.

![combobox fallback animation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/trg90wwomhyk8c17s1e8.gif)

## Fin
And that's a wrap, the Popover API should start landing in stable browsers relatively soon but Anchor positioning might take a little while longer. That being said, it really is an exciting time to be a web dev. Lots of tasks that previously required hacky workarounds are now being addressed, freeing us to explore more creative possibilities! 

Thanks so much for reading. If you'd like to connect with me outside of Dev here are my [twitter](https://twitter.com/Link2Twenty) and [linkedin](https://www.linkedin.com/in/andrew-bone-ba241b179/) come say hi 😊.