← Back to Index

Lesson 17

Anchor Positioning - Position Relative to Any Element

Traditional CSS positioning gives you three reference frames: the normal flow, a positioned parent (position: relative), or the viewport (position: fixed). But what if you want to position a tooltip relative to a button that's nowhere near it in the DOM? Previously: JavaScript. Now: CSS Anchor Positioning.

The mental model

Two roles:

The breakthrough: The positioned element doesn't need to be inside or adjacent to the anchor in the DOM. It can be anywhere in the document - it references the anchor by name. This decouples visual position from DOM structure.

Basic anchor positioning - tooltip above a button

The tooltip is positioned relative to the button using anchor() - no JS, no position: relative parent needed

I'm the anchor
Tooltip above the button
/* Step 1: Name the anchor */
.button {
  anchor-name: --my-button;
}

/* Step 2: Position relative to it */
.tooltip {
  position: absolute;
  position-anchor: --my-button;     /* which anchor to reference */
  bottom: anchor(top);              /* my bottom edge = anchor's top edge */
  left: anchor(center);             /* my left edge = anchor's horizontal center */
  translate: -50% 0;                /* shift left by half my width (center me) */
  margin-bottom: 8px;               /* gap between tooltip and anchor */
}

How anchor() works

/* anchor() takes a side of the anchor element: */
anchor(top)      /* the anchor's top edge position */
anchor(bottom)   /* the anchor's bottom edge position */
anchor(left)     /* the anchor's left edge position */
anchor(right)    /* the anchor's right edge position */
anchor(center)   /* the anchor's center */

/* Used in inset properties: */
top: anchor(bottom);       /* "my top = anchor's bottom" → below the anchor */
bottom: anchor(top);       /* "my bottom = anchor's top" → above the anchor */
left: anchor(right);       /* "my left = anchor's right" → to the right */
right: anchor(left);       /* "my right = anchor's left" → to the left */

Reading anchor() declarations: The property you're setting is YOUR edge. The value inside anchor() is THEIR edge. So top: anchor(bottom) means "place my top edge at their bottom edge" - i.e. position me below them.

The position-area shorthand

All 9 positions in the 3x3 grid around the anchor

ANCHOR (center center) top left top center top right center left center right bottom left bottom center bottom right
.anchor { anchor-name: --target; }
.label { position: absolute; position-anchor: --target; }

/* The full 3x3 grid of positions: */
.tl { position-area: top left; }
.tc { position-area: top center; }
.tr { position-area: top right; }
.cl { position-area: center left; }
.cr { position-area: center right; }
.bl { position-area: bottom left; }
.bc { position-area: bottom center; }
.br { position-area: bottom right; }
Ad

The anchor follows the target - live demo

Drag the blue button - the tooltip follows it automatically (pure CSS positioning)

Drag me
I follow via CSS!
/* CSS - the tooltip is anchored to the button */
.button { anchor-name: --drag-btn; position: absolute; }
.tooltip {
  position: absolute;
  position-anchor: --drag-btn;
  bottom: anchor(top);
  left: anchor(center);
  translate: -50% 0;
  margin-bottom: 8px;
}

// JS - only handles the drag. Tooltip follows automatically via CSS.

The key insight here: The JavaScript only moves the button. The tooltip has zero JS - it follows the button purely through CSS anchor positioning. If the anchor moves (via drag, scroll, resize, animation), the positioned element tracks it automatically.

position-try - fallback positions

What if the tooltip would overflow the viewport? position-try-fallbacks lets you define fallback positions the browser can try automatically:

.tooltip {
  position: absolute;
  position-anchor: --edge-btn;
  bottom: anchor(top);          /* preferred: above the button */
  left: anchor(center);
  translate: -50% 0;

  /* If above doesn't fit, try below */
  position-try-fallbacks: flip-block;
}

/* position-try-fallbacks values: */
/* flip-block        → flip vertically (above ↔ below) */
/* flip-inline       → flip horizontally (left ↔ right) */
/* flip-block flip-inline → try both flips */

/* Or define a CUSTOM fallback position: */
@position-try --below {
  bottom: auto;
  top: anchor(bottom);
  margin-bottom: 0;
  margin-top: 8px;
}

.tooltip {
  position-try-fallbacks: --below;
}

Why this matters: Previously, tooltip/popover libraries used JS to detect overflow and flip position. Anchor positioning with position-try-fallbacks does this natively in CSS - the browser checks if the element overflows its containing block and automatically tries fallback positions.

The Popover API + top layer

Anchor-positioned elements are position: absolute, which means they participate in stacking contexts. The Popover API solves z-index issues by rendering in the browser's top layer - above ALL stacking contexts.

Click the button - the popover opens in the top layer, positioned via anchor CSS

Popover content

I'm in the top layer. Click outside to dismiss.

<button popovertarget="my-popup">Open</button>
<div id="my-popup" popover>Content</div>

button { anchor-name: --pop-trigger; }

[popover] {
  position: absolute;
  position-anchor: --pop-trigger;
  top: anchor(bottom);
  left: anchor(left);
  margin-top: 4px;
  position-try-fallbacks: flip-block;
}

What you get for free with popover (no JS):

anchor-name rules

Browser support

Anchor positioning: Chrome 125+, Edge 125+. Not yet in Firefox or Safari (as of mid-2025). Progressive enhancement - without support, elements fall back to their normal position.

Retrieval check

Question 1

What property do you put on the reference element (the one you're positioning relative to)?

position-anchor
anchor-name
anchor-target
position: anchor

Question 2

What does bottom: anchor(top) mean?

Place me at the bottom-top corner
Place my bottom edge at the anchor's top edge (= above the anchor)
Place me at the top of the viewport bottom
The anchor's bottom becomes my top

Question 3

What does position-try-fallbacks: flip-block do?

Flips the element's content upside down
Tries positioning as display: block first
If preferred position overflows, tries flipping vertically (above / below)
Blocks the element from being positioned

Question 4

Does the positioned element need to be a DOM child of the anchor?

Yes - it must be inside the anchor element
Yes - it must be a direct sibling
No - it can be anywhere in the document (references by name)
Only if using position: fixed

Question 5

What position value must the anchored element have?

position: relative
position: static
position: absolute or position: fixed
position: anchor