Skip to content
Carl Victor Fontanos
Carl Victor Fontanos

Carl Victor Fontanos

Software Engineer

I build web applications and share what I learn along the way.

© 2026

The popover Attribute: Dropdowns Without the JavaScript

C
Carlo Fontanos
· 2 min read

Count what a dropdown menu needs: open on click, close on outside click, close on Escape, render above everything, return focus sensibly. That's four event listeners and a z-index prayer - or, since 2024, two HTML attributes:

<button popovertarget="user-menu">Account ▾</button>

<div id="user-menu" popover>
    <a href="/profile">Profile</a>
    <a href="/settings">Settings</a>
    <a href="/logout">Sign out</a>
</div>

Zero JavaScript. The button toggles the popover; clicking anywhere else or pressing Escape closes it ("light dismiss"); it renders in the top layer, above every z-index on the page - the same layer dialog.showModal() uses. Opening one auto-closes others, so exclusive menus come free.

Styling the states

[popover] {
    inset: unset;               /* popovers are centered by default - undo for menus */
    margin: 0;
    border: 0.5px solid #e2e8f0;
    border-radius: 12px;
    box-shadow: 0 8px 30px rgb(2 6 23 / 0.12);
}

[popover]:popover-open {
    /* open-state styles; pair with @starting-style for entry animation */
}

The default UA styling centers popovers like mini-dialogs; menus want inset: unset plus positioning. For entry/exit transitions from display:none, this pairs with @starting-style - the two features were practically designed together.

auto vs manual, and the JS hooks

  • popover (= popover="auto"): light dismiss, one-at-a-time. Menus, selects, tooltips.
  • popover="manual": stays open until explicitly closed, several can coexist. Toasts and persistent panels.
menu.showPopover();  menu.hidePopover();  menu.togglePopover();

menu.addEventListener('toggle', (e) => {
    if (e.newState === 'open') loadMenuData();   // lazy-load contents on open
});

The toggle event with newState is the tasteful place for lazy loading - fetch the notification list when the panel opens, not on page load.

Positioning honesty

The popover appears in the top layer but doesn't automatically anchor to its button. Fixed positioning near the trigger works for simple cases; true tethering is what CSS anchor positioning does (shipping in Chromium, in progress elsewhere) - until it's everywhere, a tiny position-on-toggle function or a floating library covers complex cases. For plenty of UI (centered quick-switchers, toasts, full-width mobile sheets) you don't need anchoring at all.

Support: Chrome 114+, Safari 17+, Firefox 125+ - all mainstream. Combined with dialog for modals and details for disclosure, the platform now covers the whole "things that open and close" category that used to justify a UI library. The library's remaining job is positioning math, and CSS is coming for that too.

C
Written by Carlo Fontanos

Full-stack web developer sharing practical tutorials and building tools that ship.

Got something on your mind?

My inbox is open - no forms disappearing into the void here.

  • Just say hello Found a tutorial useful? Spotted a mistake? Tell me.
  • Hire me for a project Have something custom in mind? Let's talk scope and timelines.
  • Product support Bought something here? I'll help you get it running.

I usually reply within 1-2 business days.

Message sent!

Your details are only used to reply to you.

Keep reading