Skip to content

CSS Anchor Positioning: Tooltips Without JavaScript

Positioning Tooltips With CSS Anchor Positioning

The CSS anchor-name and position-anchor properties tie one element's position to another, replacing Floating UI and Popper.js for common tooltip cases.

· · 4 min read

Quick Take

I've installed Floating UI in more projects than I can count, for the exact same job every time: keep a tooltip attached to its trigger button. CSS anchor positioning does that job natively now, for the common cases.

Every tooltip and dropdown library exists to solve one problem: keep element B positioned relative to element A, correctly, even as the page scrolls, resizes, or the anchor moves. That's a surprisingly involved calculation once you account for viewport edges, and it's exactly the calculation CSS anchor positioning now does natively.

TL;DR: anchor-name on a trigger element and position-anchor plus anchor() on a positioned element tie the two together natively in CSS, no JavaScript position calculation required. Combined with the HTML popover attribute for top-layer rendering and light-dismiss, this covers the common tooltip and dropdown pattern that Popper.js and Floating UI existed to solve, for browsers that support it.

.trigger-button {
  anchor-name: --my-tooltip-anchor;
}

.tooltip {
  position: fixed;
  position-anchor: --my-tooltip-anchor;
  top: anchor(bottom);
  left: anchor(center);
  translate: -50% 8px;
}
<button class="trigger-button">Hover me</button>
<div class="tooltip" popover>Helpful tooltip text</div>

anchor-name gives the button a named anchor reference. position-anchor on the tooltip points at that name, and anchor(bottom)/anchor(center) compute the tooltip's top/left relative to the anchor's actual rendered position, bottom edge and horizontal center respectively. This recalculates automatically on scroll and resize, the same behavior Floating UI's autoUpdate() exists to provide manually.

Fallback Positions With @position-try

.tooltip {
  position: fixed;
  position-anchor: --my-tooltip-anchor;
  top: anchor(bottom);
  left: anchor(center);
  translate: -50% 8px;
  position-try-fallbacks: flip-block;
}

@position-try flip-block {
  top: anchor(top);
  bottom: unset;
  translate: -50% -8px;
}

position-try-fallbacks: flip-block tells the browser: if the tooltip would overflow the viewport in its preferred position (below the anchor), try the flip-block fallback instead (above the anchor). Several built-in try-tactic keywords exist (flip-block, flip-inline, flip-start) covering the common flip cases without a custom @position-try block at all, worth checking before writing a manual fallback.

Combining With popover for a Full Dropdown

<button popovertarget="user-menu" class="menu-trigger">Account</button>
<div id="user-menu" popover class="dropdown-menu">
  <a href="/profile/">Profile</a>
  <a href="/settings/">Settings</a>
  <a href="/logout/">Log out</a>
</div>
.menu-trigger {
  anchor-name: --user-menu-anchor;
}

.dropdown-menu {
  position-anchor: --user-menu-anchor;
  top: anchor(bottom);
  right: anchor(right);
  margin-top: 4px;
}

popovertarget on the button wires up open/close behavior, focus handling, and light-dismiss (clicking outside closes it) entirely through HTML attributes, no JavaScript addEventListener needed for that part either. anchor-name/position-anchor handle where it appears. Together, this is a complete dropdown menu, positioning, top-layer rendering, dismiss behavior, and keyboard accessibility, without a single line of JavaScript or a dependency.

Sizing the Positioned Element to Match Available Space

.dropdown-menu {
  position-anchor: --user-menu-anchor;
  top: anchor(bottom);
  right: anchor(right);
  max-height: anchor-size(height); /* size relative to the anchor's own height, if useful */
  max-width: 320px;
  overflow-y: auto;
}

anchor-size() reads a dimension from the anchor element itself, useful for a dropdown that should match its trigger's width exactly (width: anchor-size(width);), rather than having a fixed or content-based width unrelated to the button that opened it.

Browser Support and the JavaScript Fallback Story

Anchor positioning shipped in Chrome and Edge in 2024, with Firefox and Safari following through 2025 and into 2026. For the remaining gap, this is a feature where a JavaScript fallback still matters more than most CSS features covered elsewhere in this series, an unsupported browser doesn't gracefully degrade to "no positioning," it renders the tooltip at its default static position, likely overlapping other content. Feature-detect with @supports (anchor-name: --a) and load Floating UI conditionally only for browsers that fail that check, rather than assuming universal support the way you reasonably could with :has() or scroll-driven animations by 2026.

Old Way vs Modern Way

TaskBeforeWith anchor positioning
Tooltip stays attached to its triggerFloating UI / Popper.js, JS position calcanchor-name + anchor()
Recalculate on scroll/resizeManual autoUpdate() listenerAutomatic, native
Flip to avoid viewport overflowManual collision detection logicposition-try-fallbacks
Dropdown open/close, focus, dismissCustom JS or a UI libraryHTML popover attribute

Conclusion

Anchor positioning doesn't add a capability CSS never had a workaround for, it removes the JavaScript dependency that workaround required. For a straightforward tooltip or dropdown, anchor-name/anchor() combined with the popover attribute covers what Floating UI existed to solve, with the caveat that support isn't universal enough yet to drop the JavaScript fallback for every project without checking your actual browser support requirements first.

Frequently Asked Questions

What does CSS anchor positioning actually do?
It lets one element (the positioned element, like a tooltip) declare its position relative to a different element (the anchor, like a button) using CSS alone, via anchor-name on the anchor and position-anchor plus inset properties referencing anchor() on the positioned element. Before this, keeping a tooltip attached to a button that could be anywhere on the page, and recalculating that position on scroll or resize, required a JavaScript library computing coordinates on every relevant event.
Does anchor positioning replace the popover attribute?
No, they solve different problems and are commonly used together. The HTML popover attribute handles top-layer rendering, focus management, and light-dismiss behavior (closing when you click outside), CSS anchor positioning handles where the popover appears relative to its trigger. A popover without anchor positioning still needs manual position calculation; anchor positioning without popover still needs manual top-layer/focus handling. Combined, they cover the whole tooltip/dropdown pattern natively.
What happens when there's no room for the tooltip in its preferred position?
The @position-try at-rule lets you define fallback positions, and the browser automatically switches to one when the preferred position would overflow the viewport, a tooltip that prefers to open above its anchor but is too close to the top of the screen can fall back to opening below, without any JavaScript resize/scroll listener recalculating this manually.