unscripted/ui

Tooltip

Two tiers — a pure :hover/:focus tooltip that works everywhere, and interest-invoker toolbar tips with smart delays and viewport-aware flipping.

interest invokersanchor positioningexit animations

There are two ways to build a tooltip here. The plain-CSS one works in every browser today and is the right default for a lone control. The interest-invoker one is cutting-edge (Chromium only, for now) and earns its keep the moment you have a group of controls, because it can dynamically adjust the display delay.

Plain CSS - maximum compatibility

The safe default. No new platform features, no build caveats, works everywhere.

  • A group/tip wrapper toggles the tip via group-hover/tip: and group-focus-within/tip:, so keyboard users get it too.
  • delay-100 waits a beat before showing, so a cursor passing through doesn’t flash it.
  • pointer-events-none keeps the tip from stealing hover and flickering itself.

The trade-off: because it’s positioned by hand, it can’t flip at a viewport edge, so keep tips short. And, as the next section explains, a row of these can’t get the toolbar delay behavior right. For one button in the middle of a form, this is all you need.

Interest invokers - cutting edge, built for groups

The editor-toolbar pattern: the first tooltip waits 300ms, but once one is showing, moving to the next button switches instantly. The delay resets the moment you move the mouse away.

  • interestfor on each button points at its tip, a popover="hint" element. The browser handles showing on hover and keyboard focus, hiding, delays, and the top layer. No group/peer wiring, no pointer-events guard.
  • The delay trick (via Emil Kowalski), written once on the wrapper: [&>button]:[interest-delay-start:0.3s] makes the first tooltip wait, and [&:has(:interest-source)>button]:[interest-delay-start:0s] zeroes the delay while any sibling already shows interest. Moving between buttons switches tips instantly. The default interest-delay-end keeps a button’s interest “warm” for a moment after you leave it. That overlap is what makes the switch feel instant, so don’t set it to 0s.
  • Every button declares its own [anchor-name:--tt-*] and its tip’s [position-anchor:--tt-*] points back at it, so [position-area:top] centers each tip over its own button.
  • [position-try-fallbacks:flip-block] keeps tips on screen: when a tip would poke past the top of the viewport, the browser flips it below the button automatically.

The fallback story

In browsers without interest invokers the toolbar’s hover tips never appear. The buttons keep their aria-labels, so assistive tech loses nothing. If you need hover tooltips for everyone today, use the plain-CSS variant. A row of them will then share one fixed delay rather than the adaptive toolbar timing.

Browser support

Minimum stable version per engine, resolved at build time from MDN's browser-compat-data; Baseline status from the official web-features dataset. Everything degrades gracefully — the “when missing” column is the actual behavior, not a broken page.

FeatureChromeEdgeFirefoxSafariWhen missing
Interest invokers (interestfor + popover="hint")Limited availability142142The hover UI simply never appears — triggers keep their aria-labels and links still navigate, so nothing is lost. If you need it everywhere today, the plain-CSS technique on the Tooltip page works in every browser.
CSS anchor positioningLimited availability12912914726The popover opens centered in the viewport (the top-layer default) instead of attached to its trigger.
Entry/exit animations (allow-discrete + @starting-style)Baseline 2024 · newly available11711712917.5Elements appear and disappear instantly. Nothing breaks — you just lose the animation.

† @supports cannot test at-rules, so the badge checks transition-behavior: allow-discrete, which co-shipped with @starting-style in every engine.