Articles /

Experimentalnew-feature

Anchor positioning

The CSS feature that ends the era of Popper.js for tooltips and floating UI.

The problem anchor positioning solves

Floating elements — tooltips, popovers, dropdowns, select panels — need to be positioned relative to a trigger element. The trigger might be anywhere in the DOM, scroll around, or be inside a stacking context. JavaScript libraries like Popper.js and Floating UI exist entirely to compute this positioning, handle scroll, and flip when the floating element would go off-screen.

CSS Anchor Positioning is the native solution. You name an anchor, reference it by name, and the floating element positions itself relative to that anchor. The browser handles scroll, resize, and basic overflow avoidance.

Core syntax

/* 1. Name the anchor */
.trigger {
  anchor-name: --my-tooltip;
}

/* 2. Position relative to it */
.tooltip {
  position: fixed; /* escape stacking contexts with fixed */

  /* Align tooltip's bottom to anchor's top */
  inset-block-end: anchor(--my-tooltip top);

  /* Center on anchor */
  inset-inline-start: anchor(--my-tooltip center);
  translate: -50% -100%;

  /* Gap between tooltip and anchor */
  margin-block-end: 0.4rem;
}

The anchor() function takes a named anchor and an edge (top, bottom, left, right, center, start, end, self-start, self-end). Logical properties (inset-block-end, inset-inline-start) are preferred for writing-mode support.

Placement variants

/* Above */
.tooltip-top {
  inset-block-end: anchor(--trigger top);
  inset-inline-start: anchor(--trigger center);
  translate: -50% -100%;
  margin-block-end: 0.4rem;
}

/* Below */
.tooltip-bottom {
  inset-block-start: anchor(--trigger bottom);
  inset-inline-start: anchor(--trigger center);
  translate: -50% 0;
  margin-block-start: 0.4rem;
}

/* To the right */
.tooltip-right {
  inset-block-start: anchor(--trigger center);
  inset-inline-start: anchor(--trigger right);
  translate: 0 -50%;
  margin-inline-start: 0.4rem;
}

Auto-placement with @position-try

CSS Anchor Positioning includes @position-try and position-try-fallbacks so the browser can flip placement if the tooltip would overflow. This replaces Popper.js's automatic flipping.

/* Define fallback placements */
@position-try --below {
  inset-block-end: auto;
  inset-block-start: anchor(--trigger bottom);
  translate: -50% 0;
  margin-block-end: 0;
  margin-block-start: 0.4rem;
}

.tooltip {
  /* Try default (above) first, then --below if it overflows */
  position-try-fallbacks: --below;
}

Pairing with the Popover API

Anchor positioning pairs naturally with the HTML Popover API — the browser manages open/close state, focus trapping, and light-dismiss. CSS positions it.

<button popovertarget="my-popover" style="anchor-name: --pop-trigger">
  Open popover
</button>
<div id="my-popover" popover>
  Popover content
</div>
#my-popover {
  position: fixed;
  position: anchor(--pop-trigger bottom);
  inset-inline-start: anchor(--pop-trigger left);
  margin-block-start: 0.4rem;
}

Fallback

/* Always provide a fallback */
@supports not (anchor-name: --x) {
  .trigger { position: relative; }
  .tooltip {
    position: absolute;
    bottom: calc(100% + 0.4rem);
    left: 50%;
    translate: -50% 0;
    inset-block-end: auto;
    inset-inline-start: auto;
  }
}
Browser support in 2026: Chrome 125+, Edge 125+. Safari is implementing it. Use as a progressive enhancement with a position: absolute fallback.