For years, positioning a tooltip next to its trigger required JavaScript. You needed a library — Floating UI, Popper.js, Tippy.js — to calculate coordinates, detect viewport edges, flip directions on overflow, and reposition on scroll. The need was real and the tooling was competent, but the dependency was always a workaround for a gap in CSS itself. It’s part of a broader pattern of CSS absorbing responsibilities that used to require JavaScript — cascade layers did the same thing for specificity management that anchor positioning now does for floating-element placement.

That gap is now closed. The CSS Anchor Positioning API reached Baseline 2026 status with support across Chrome 125+, Firefox 132+, and Safari 18.2+. For the first time, a stylesheet can describe “place this element relative to that other element” without a single line of JavaScript.

The Core Model: Anchors and Positioned Elements

Anchor positioning works with two roles. An anchor element is any element you designate as a reference point. A positioned element is an absolutely-positioned element that attaches itself to the anchor.

The anchor is declared with anchor-name, which takes a dashed-ident value — a custom identifier prefixed with two hyphens:

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

The positioned element connects to that anchor with position-anchor:

.tooltip {
  position: absolute;
  position-anchor: --my-tooltip-anchor;
}

From this point on, the tooltip can reference the anchor’s edges using the anchor() function inside any inset property (top, bottom, left, right, or their logical equivalents):

.tooltip {
  position: absolute;
  position-anchor: --my-tooltip-anchor;
  bottom: calc(anchor(top) + 8px);
  left: anchor(left);
}

Here anchor(top) resolves to the computed y-coordinate of the anchor’s top edge, and anchor(left) resolves to its left edge. The tooltip sits 8 pixels above the button, left-aligned with it — with no JavaScript involved.

position-area: A Shorthand for Common Placements

Writing explicit anchor() calls for every edge is precise but verbose for common cases. The position-area property offers a shorthand using a 3×3 grid where the anchor element occupies the center cell:

.tooltip {
  position: absolute;
  position-anchor: --my-tooltip-anchor;
  position-area: top center;
}

The grid accepts keywords like top, bottom, left, right, center, block-start, block-end, inline-start, and inline-end. position-area: top center places the element in the top-center cell — directly above the anchor, horizontally centered. position-area: bottom span-all spans the full bottom row. Physical and logical keywords can be mixed to match your writing mode requirements.

position-area is ideal for standard placements where the exact pixel math of anchor() would be excessive. Both approaches are valid; choose based on the complexity of your layout.

Handling Viewport Overflow With @position-try

One reason JavaScript libraries existed: CSS could not detect when a positioned element would overflow the viewport and automatically reposition. @position-try changes that.

You define one or more fallback positions as named try-blocks, then list them in position-try-fallbacks:

@position-try --flip-above {
  position-area: top center;
}

@position-try --flip-below {
  position-area: bottom center;
}

.tooltip {
  position: absolute;
  position-anchor: --my-tooltip-anchor;
  position-area: top center;
  position-try-fallbacks: --flip-above, --flip-below;
}

The browser tries --flip-above first. If the element would overflow, it tries --flip-below. It applies the first fallback that fits within the viewport. If no fallback fits, the original position is used.

There are also built-in flip keywords that handle the most common cases without writing explicit try-blocks:

.tooltip {
  position: absolute;
  position-anchor: --my-tooltip-anchor;
  position-area: top center;
  position-try-fallbacks: flip-block;
}

flip-block flips the element to the opposite side on the block axis (above to below). flip-inline flips on the inline axis. flip-start flips the start/end sides. These cover the behavior of Floating UI’s flip() middleware in a single CSS declaration.

Full @position-try support requires Chrome 125+, Firefox 147+, and Safari 26+. The core anchor placement (anchor-name, position-anchor, anchor()) works from Safari 18.2 onward; the flip fallback rules require the updated Safari 26 release.

Pairing With the Popover API

Anchor positioning handles where a floating element appears. It does not handle when to show or hide it. That is where the Popover API completes the picture.

<button popovertarget="my-tooltip">Hover me</button>

<div id="my-tooltip" popover>
  This is the tooltip content.
</div>
.trigger-button {
  anchor-name: --tooltip-anchor;
}

#my-tooltip {
  position: absolute;
  position-anchor: --tooltip-anchor;
  position-area: top center;
  position-try-fallbacks: flip-block;
  margin: 0;
}

The popover attribute gives you: Escape key dismissal, light-dismiss on click outside, top-layer stacking (no z-index warfare), and focus management. CSS anchor positioning gives you: declarative placement with automatic viewport overflow handling.

Together they replace what previously required a JavaScript library, a dependency update cycle, and several hundred lines of positioning logic.

Progressive Enhancement and Feature Detection

Because CSS Anchor Positioning is Baseline 2026 rather than universally shipped, a @supports check is appropriate for production use:

@supports (anchor-name: --test) {
  .tooltip {
    position: absolute;
    position-anchor: --my-tooltip-anchor;
    position-area: top center;
    position-try-fallbacks: flip-block;
  }
}

Browsers that do not support anchor positioning ignore the entire block. Your fallback can be a display: none default on the tooltip or a JavaScript-based positioning approach for unsupported browsers. The OddBird CSS Anchor Positioning polyfill extends support back to Firefox 54, Chrome 51, Edge 79, and Safari 10 for teams that need broader coverage.

The Baseline 2026 designation reflects 91% global browser coverage across Chromium, Firefox, and Safari engines — a threshold that supports shipping without a polyfill for most production contexts.

What This Changes for Frontend Practice

The immediate practical shift: any tooltip, dropdown, context menu, popover, or floating label that currently depends on a JavaScript positioning library is a candidate for replacement with CSS alone.

Beyond eliminating a dependency, native anchor positioning carries two structural advantages. First, the browser’s layout engine handles geometry calculations rather than JavaScript executing after paint — which removes a source of the layout-then-reposition flash that JavaScript-positioned elements sometimes produce. Second, the positioned element participates correctly in the stacking context from the start, since the Popover API places it in the top layer automatically.

The more architectural shift is in how you model floating UI components. Previously, a tooltip component needed initialization code, event listeners, resize observers, and scroll listeners. With anchor positioning and the Popover API, the component is markup and stylesheet — the browser manages the behavior.

There will still be cases where JavaScript is the right tool: programmatically reading position values, animating along computed paths, or integrating with state management. But “position this element next to that element” is no longer one of them.