# Popovers

> Which Smootify parts can open in a popover, how to build one with two attributes, and the rules for option pickers in a popover.

Status: Ready (Smootify 2.0)  
URL: https://www.smootify.io/docs/astro/build/popovers  
Updated: 2026-10-21

A popover is a panel that opens above the page, such as a menu, a list of choices or a drawer. The browser opens and closes it: Escape and a click outside close it, and it moves the focus for keyboard and screen reader users. Every panel Smootify opens or closes can be a popover, from the mini cart to the option pickers, the filters and the cookie banner.

> [!NOTE]
> Popovers work in Safari 17, Chrome 114, Firefox 125 and later.

## Build a popover

A popover is two attributes, and Smootify works the same with every popover you write:

1. The panel is an element with the attribute `popover="auto"` and an `id`, such as `size-list`.
2. The trigger is a `<button popovertarget="size-list">`, with the `id` of the panel.
3. Inside a form, such as the add-to-cart form, also give the trigger `type="button"`. Without it, the trigger submits the form.
4. To close the panel from inside, add a button with the same `popovertarget` and `popovertargetaction="hide"`. Escape and a click outside also close it.

**How it looks.**

- The browser shows the panel above everything, in the middle of the window. Place it with your own styles, such as a fixed position on the right for a drawer.
- Smootify takes the browser’s padding and border off every popover, and the class of your panel sets its own. There is no backdrop: the page behind stays as it is. For a backdrop, such as behind a drawer, set the CSS variable `--smootify-backdrop-color` on the panel’s class, for example `rgba(0, 0, 0, 0.5)`. See [CSS](https://www.smootify.io/docs/astro/developers/css).
- **A panel that is a popover only on small screens**, such as filters in a column on desktop and in a drawer on mobile: on the panel’s class, set `--smootify-closed-display: flex` at the desktop breakpoint, with `position: static`, `inset: auto`, `margin: 0` and the column’s width, since the browser places every popover fixed in the middle. Set `--smootify-closed-display: none` at tablet, so it is a drawer again.
- **The page under a drawer.** A popover that covers the page, such as a cart drawer or a full-screen menu, takes the attribute `data-overflow-body`: the page stops scrolling while it is open, and its scrollbar keeps its space so nothing jumps. A popover anchored to its trigger, such as a small menu under a button, leaves it out. It works on a `<dialog>` too.
- To animate a popover, use CSS: `@starting-style` and `transition-behavior: allow-discrete` on the panel, as in [Write components](https://www.smootify.io/docs/astro/build/components). The transition plays whoever opens or closes the popover: your trigger, Smootify (the cart after an add to cart, the market suggestion), Escape or a click outside. Smootify adds no animation of its own.

## Which parts can use a popover

Each of these parts can open in a popover:

| Element | What the popover holds | Guide |
|---|---|---|
| `smootify-cart` | the mini cart | [Build the cart](https://www.smootify.io/docs/build/cart) |
| `variant-swatches` | the value buttons; closes after a pick | [Product page](https://www.smootify.io/docs/build/product-page) |
| `variant-popover` | the values of an option | [Product page](https://www.smootify.io/docs/build/product-page) |
| `addon-popover` | the variants of an add-on | [Product add-ons](https://www.smootify.io/docs/features/add-ons) |
| `configurator-popover` | a configurator option | [Product configurator](https://www.smootify.io/docs/features/product-configurator) |
| `dynamic-property` (Dropdown type) | the choices | [Dynamic properties](https://www.smootify.io/docs/features/dynamic-properties) |
| `filter-popover`, `sort-popover` | a filter's values, the sort choices | [Filters](https://www.smootify.io/docs/build/filters) |
| `country-switcher`, `location-switcher` | the countries, the locations | [Markets](https://www.smootify.io/docs/features/markets) |
| `smootify-search` | the results; opens as the visitor types | [Predictive search](https://www.smootify.io/docs/build/predictive-search) |
| `store-availability` | the other locations, loaded when it opens | [Pickup](https://www.smootify.io/docs/features/store-availability) |
| `klaviyo-back-in-stock` | the alert form | [Back in stock](https://www.smootify.io/docs/features/back-in-stock) |
| `smootify-consent` | the cookie banner | [Cookie consent](https://www.smootify.io/docs/features/cookie-consent) |
| the market suggestion | is the popover itself; Smootify opens it a few seconds after the page loads | [Market suggestion](https://www.smootify.io/docs/features/market-suggestion) |

On a collection page, `auto-close-details="true"` on `smootify-search-discovery` closes every popover inside it each time the results change, such as a filters panel. That includes a popover inside a product card.

## Pickers in a popover

These rules apply to `variant-popover`, `addon-popover`, `filter-popover`, `sort-popover`, `configurator-popover`, `country-switcher`, `location-switcher` and the Dropdown type of `dynamic-property`:

- **The trigger goes inside the element.** Its text shows the current choice, so it changes when the visitor picks. A fixed label, such as Size, goes outside it.
- **The first link or button inside the popover is the template** of every value. Smootify copies it and removes the links or buttons next to it. A button with `popovertarget` does not count.
- **A pick closes the popover**, as do Escape and a click outside. A close button inside needs `popovertarget` and `popovertargetaction="hide"`, or it becomes the template.

The attributes are the ones of the `*-dropdown` version of each picker, such as `data-option` on `variant-popover`.

```html
<div>Size</div>
<variant-popover data-option="1">
  <button type="button" popovertarget="size-list"><span>M</span></button>
  <div id="size-list" popover="auto">
    <a href="#"><span option="title">Value</span></a>
  </div>
</variant-popover>
```

Here the panel has the `id` `size-list`. The label “Size” sits outside the trigger, because the trigger’s text becomes the chosen value.

## Popovers in repeated blocks

A popover can sit inside a block Smootify repeats, such as an address, an order or a product card. Each copy gets its own `id`, and its trigger follows, so each trigger opens its own panel. Design the popover once. On product cards that Astro renders on the server, give each card’s popover its own `id` and point the trigger’s `popovertarget` at it: Smootify never reuses an `id` the page already has.

In a mini cart, a popover anywhere inside `smootify-cart` makes Smootify open the whole cart as a popover, so keep popovers out of the lines.

## Before you publish

> [!STUCK]
> - **The trigger adds to cart or submits the form.** It is inside a form without `type="button"`.
> - **A close button became a value, or disappeared.** It is the first button in the popover, so it became the template. Close with a button that has `popovertarget` and `popovertargetaction="hide"`, or leave it to Escape and the click outside.
> - **The label on the trigger is replaced.** The trigger's first text shows the choice. Put a fixed label outside the trigger, or after the text of the choice.
> - **The choice never shows on the trigger.** The trigger is outside the picker element.
> - **The picker disappears.** It has no popover, or the popover has no link or button inside.
> - **The trigger does nothing.** Its `popovertarget` is not the exact `id` of the panel.
> - **The panel opens in the middle of the window.** It has no position of its own: place it with your styles.
> - **The panel never shows on desktop.** A closed popover is hidden: set `--smootify-closed-display` on its class at that breakpoint.
> - **Every card opens the same panel.** The cards are rendered on the server with one `id` for all their popovers. Give each card's popover its own `id`.
