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.

Hover or focus to show tooltip
I'm a tooltip
Another tooltip
Uses popover=hint + interestfor (shown with JS fallback)

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.

Styled & animated tooltips
💾 Save your work
⚠️ This cannot be undone
📤 Share with others
⚙️ App preferences

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.

Reusable tooltip system — multiple positions
Positioned above
Positioned right
Positioned below
Positioned left
Each tooltip uses CSS anchor positioning for its direction

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.