💬

Popover API — Why You Can Now Build Tooltips Without JavaScript

A single popover attribute handles open/close, Esc dismissal, and accessibility automatically

<button popovertarget="tip-1">Hover me</button>
<div id="tip-1" popover="auto" role="tooltip">
  This is a native tooltip
</div>

Zero lines of JavaScript. No event listeners. No state tracking. Yet it dismisses with Esc, supports keyboard navigation, and screen readers announce it correctly.

How Traditional Tooltips Break

"Show on hover, hide on leave" sounds simple. Deploy it and problems pile up.

Keyboard users Tab to the trigger — nothing appears. Screen readers read it twice or not at all. Fast mouse movement causes flickering. Small screens cause overlap. Esc doesn't dismiss. Focus gets lost.

Event listeners accumulate. Separate hover/focus handling. Outside-click edge cases. Manual ARIA synchronization. The code bloats. Not because the implementation is bad, but because the web platform had no concept of "this element is a tooltip."

What Popover API Changed

Add the popover attribute and the browser understands the element's role. Three things work automatically.

1. Open/close without JavaScript

popovertarget="id" connects button to popover. Done. Browser handles click and focus interactions. popovertargetaction specifies behavior — show, hide, or toggle (default).

2. Automatic Esc handling

No global keydown handler. No Esc-specific cleanup logic. The browser recognizes popovers as dismissible elements.

3. Automatic ARIA sync

aria-expanded updates automatically with popover open/close. No manual management. No stale state risk. Lighthouse stops warning about incorrect ARIA — there are no custom ARIA states to get wrong.

auto vs manual

auto closes on outside click, Esc, or when another auto popover opens. Good for tooltips.

manual requires explicit dismissal. Focus restoration isn't automatic either — you must return focus to the trigger manually on blur. Good for toast notifications.

Where JavaScript Is Still Needed

Timing control. Native popovers open/close instantly. Fast mouse movement still causes flicker. Delay between hover and open requires JavaScript.

Hover intent. The browser can't know why a pointer is over an element — intentional hover or passing through. JavaScript handles this.

But the core shifted. JavaScript no longer owns the entire interaction model. Popover API handles base open/close; JavaScript just layers intent detection on top.

When Libraries Still Win

Complex positioning with nested scroll containers and custom flipping logic — Floating UI still wins here. CSS anchor positioning is emerging but early-stage.

Large design systems needing centralized behavior and guardrails across teams.

For most cases though — simple tooltips, dropdown menus, toast notifications — Popover API is enough. No library needed.

Key Points

1

popover + popovertarget handles open/close, Esc, keyboard navigation automatically

2

Automatic aria-expanded sync eliminates an entire category of accessibility bugs

3

auto closes on outside click/Esc, manual requires explicit control — choose by use case

4

Timing control and hover intent still need JavaScript, but the core interaction model is browser-owned

Use Cases

Simple tooltips — popover="auto" + role="tooltip", no JS needed Dropdown menus — control open/close with popovertargetaction Toast notifications — explicit control with popover="manual"