The tooltip problem
Building good tooltips is surprisingly hard. You need hover detection (mouseenter/mouseleave), focus management (focus/blur for keyboard), a delay to prevent accidental triggers, position calculation relative to the trigger element, viewport-aware flipping, and cleanup on unmount. Libraries like Tippy.js and Floating UI exist entirely because this combination is so error-prone.
Modern CSS and HTML now give us three features that, combined, replace all of that JavaScript: popover=hint for the tooltip layer, interestfor for declarative hover/focus triggering, and CSS anchor positioning for automatic placement.
Step 1: Create the popover hint
The popover=hint type is designed for ephemeral UI like tooltips. Unlike popover=auto, hint popovers don't close other open popovers — they layer naturally:
<button id="trigger">Hover me</button>
<div id="tooltip" popover=hint>
Helpful tooltip text
</div> The tooltip renders in the top layer when shown, so it won't be clipped by overflow: hidden on any ancestor.
Step 2: Wire up the interestfor trigger
The interestfor attribute on the trigger element handles all the event logic: hover with a configurable delay, keyboard focus, and even touch long-press:
<button id="trigger"
interestfor="tooltip">
Hover me
</button> That's it for the trigger. The browser shows the tooltip on hover (after a 0.5s default delay) and hides it when the user moves away. You can customize the delay:
[interestfor] {
interest-delay: 0.2s;
} Unlike commandfor (which only works on buttons), interestfor also works on <a> tags, making it ideal for link previews and hover cards.
Step 3: Position with CSS anchoring
CSS anchor positioning ties the tooltip to its trigger without any JavaScript position calculation:
#trigger {
anchor-name: --trigger;
}
#tooltip {
position: fixed;
position-anchor: --trigger;
top: anchor(bottom);
left: anchor(center);
translate: -50% 8px;
} The tooltip is now anchored below the trigger, centered horizontally. The browser keeps this position in sync automatically — no scroll listeners or resize observers needed.
Step 4: Add viewport-aware flipping
If the tooltip would overflow the viewport, use position-try-fallbacks to flip it:
#tooltip {
position-try-fallbacks: flip-block;
} This tells the browser to try flipping the tooltip to the opposite side (above the trigger) if there isn't enough room below. You can also define custom fallback positions for more complex scenarios.
Step 5: Style and animate
Style the tooltip with CSS and add entry/exit animations:
#tooltip {
background: #1a1a22;
color: #e4e4e7;
padding: 8px 14px;
border-radius: 8px;
font-size: 0.85rem;
box-shadow: 0 4px 16px rgba(0,0,0,.2);
/* Entry animation */
transition: opacity 0.2s, translate 0.2s;
transition-behavior: allow-discrete;
@starting-style {
opacity: 0;
translate: -50% 16px;
}
} Using @starting-style with transition-behavior: allow-discrete gives you smooth entry animations from the hidden state, and the browser handles the exit animation automatically.
Making it reusable
For a multi-tooltip system, use unique anchor names per trigger-tooltip pair. The attr() function with typed values can automate this:
[interestfor] {
anchor-name: attr(id type(<custom-ident>));
}
[popover=hint] {
position-anchor: attr(anchor type(<custom-ident>));
} This pattern scales to any number of tooltips without unique CSS rules per tooltip. Combined with popover=hint and interestfor, you have a complete tooltip system in pure HTML and CSS.
Browser support
The full combination of popover=hint, interestfor, and CSS anchor positioning is available in Chrome 135+ and Edge 135+. For progressive enhancement, provide a title attribute fallback on triggers so users in unsupported browsers still get native browser tooltips. The popover attribute is a no-op in browsers that don't support it, so the page won't break.