Floating menus with modern JavaScript & CSS

Learn with me

These posts document my own learning journey as I explore new web APIs, CSS features, and JavaScript capabilities. I’ll be sharing my discoveries, experiments, and insights as I figure things out—think of it as a public learning log where I work through new technologies in real-time.

The new Popover API1 offers a robust, declarative way to handle transient (temporary) user interface elements in web development. Historically, creating elements like custom tooltips, context menus, or simple notifications required complex JavaScript to manage their positioning, visibility, and, most importantly, the interaction layer—specifically, ensuring they could be dismissed when the user clicks elsewhere (light-dismiss functionality). The Popover API standardizes this by introducing the global popover attribute.

You can convert any HTML element into a popover by simply adding the popover attribute to it, often alongside an id. Then, you connect it to a control element (usually a <button>) using the popovertarget attribute, which takes the popover element’s id as its value.

<button popovertarget="my-menu">Open Menu</button>
<div id="my-menu" popover>
  <p>Hello from the popover!</p>
</div>

Popovers support the ::backdrop pseudo-class allow you to customize their background which is useful when you want to add a visual overlay behind the popover to draw attention or dim the underlying content.

Non-Modal by Design

A key architectural difference is that elements marked with popover are inherently non-modal. When a popover opens, the rest of the page remains interactive and accessible. The browser handles automatically moving the popover to the top layer—a new stacking context where it sits above all other content—and provides automatic light-dismiss behavior. For instance, if you press the Escape key or click outside the popover element, it closes without needing extra JavaScript listeners.

This non-modal nature makes the Popover API ideally suited for smaller UI components where you want to temporarily display information or options without interrupting the user’s flow. We’re talking about use cases like:

  • Custom Tooltips
  • Context/Action Menus
  • Teaching UI (e.g., small introductory bubbles)
  • “Toast” Notifications

Choosing Between Popover and Dialog

While you can technically use the Popover API to build a full-screen overlay with a backdrop, the <dialog> element remains the better tool for creating true modals.

The <dialog> element renders a ::backdrop pseudo-element and makes the underlying page content inert (unclickable).

FeaturePopover APIDialog Element
ModalityNon-modal (page remains interactive)Modal (page becomes inert) or Non-modal
Primary UseTooltips, Context Menus, small overlaysTrue Modals, complex forms, critical alerts
DismissalAutomatic (Escape key, outside click)Manual (must use JS or form submission)
Top LayerAlways placed on the top layerPlaced on the top layer when modal

For most complex or critical user interactions, stick with the <dialog> element. Use the Popover API when you want simple, standardized controls over small elements that need reliable positioning and quick dismissal.

As you can see, you can implement modals using popover API, but the page can still be scrolled behind it.

Leveraging CSS Anchors for Adaptive Positioning

One of the most powerful combinations in modern CSS is pairing the Popover API with the CSS Anchor Positioning specification. This allows you to create highly intelligent overlays that automatically adjust their position based on available screen space, all without writing a single line of positioning JavaScript.

The workflow is based on three new CSS concepts:

  1. Anchoring the Target: You use the anchor-name CSS property on the element you want the popover to attach to. This establishes the element as the “anchor.”
  2. Referencing the Anchor: Inside the popover’s CSS, you use the anchor()2 function (and related properties like anchor-center) to set the popover’s position relative to the anchor’s edges or center, rather than the main viewport.
  3. Automatic Repositioning with @position-try: This is where the magic happens. You define multiple placement options for the popover using the @position-try3 at-rule. You define a primary placement (e.g., placing the popover to the bottom-right of the anchor) and alternative placements (e.g., top, left). The browser then checks these alternatives in the order you specify using the position-try-fallbacks property.

The browser executes this logic without any JavaScript. It first attempts to place the popover using the initial CSS rules (or a defined primary attempt, often leveraging the position-area property). If that placement causes the popover to extend off the screen, it automatically moves to the first option listed in position-try-fallbacks. If that one also fails, it continues down the list. This ensures the popover always appears in the first available, visible position.

This combination of Popover API for simple control and Anchor Positioning for advanced placement eliminates the need for JavaScript rectangle calculations (getBoundingClientRect()), leading to significantly cleaner, faster, and more robust UI components.

Footnotes

  1. Popover API - MDN

  2. anchor() - MDN

  3. @position-try - MDN