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:
- Anchor - the element you're positioning relative TO (the reference point). It gets
anchor-name. - Positioned element - the element being placed (tooltip, popover, dropdown). It uses
position-anchorandanchor()functions.
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
/* 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 { 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; }
The anchor follows the target - live demo
Drag the blue button - the tooltip follows it automatically (pure CSS positioning)
/* 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):
- Top layer - above all stacking contexts, no z-index needed
- Light dismiss - clicking outside or pressing Escape closes it
- One-at-a-time - opening a new auto popover closes the previous one
- Accessible - proper ARIA semantics built in
:popover-open- CSS pseudo-class for styling the open state
anchor-name rules
- Names must start with
--(just like custom properties) - Names must be unique within the anchor scope
- The anchor must be visible (not
display: none) for positioning to work - The positioned element must be
position: absoluteorposition: fixed
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)?
Question 2
What does bottom: anchor(top) mean?
Question 3
What does position-try-fallbacks: flip-block do?
Question 4
Does the positioned element need to be a DOM child of the anchor?
Question 5
What position value must the anchored element have?