Anchor positioning
A tooltip above a button or a dropdown menu used to be placed like this: position: relative on the parent, position: absolute and coordinates on the tooltip. That works only if the tooltip sits inside the button. Anchor positioning lets you attach an element to any other element on the page. The element it is attached to is called the anchor.
An anchor and an attached element
The anchor gets a name with the anchor-name property. The name starts with two dashes, like a CSS variable. The attached element gets position: absolute and a reference to the anchor — position-anchor. The anchor() function gives the coordinate of a side of the anchor:
<style>
.btn { anchor-name: --save; margin: 40px 0 0 60px; }
.tip {
position: absolute;
position-anchor: --save;
bottom: anchor(top);
left: anchor(left);
padding: 4px 8px;
background: black;
color: white;
font-size: 14px;
}
</style>
<button class="btn">Save</button>
<span class="tip">Ctrl+S</span>bottom: anchor(top) means "the bottom of the tooltip is level with the top of the anchor". The tooltip is separate in the markup, yet it sticks to the button.
position-area — simpler
Computing sides with anchor() is not always needed. The position-area property puts the element into one of nine cells around the anchor — the anchor itself is in the middle:
<style>
.btn { anchor-name: --menu; margin: 8px 0 0 60px; }
.menu {
position: absolute;
position-anchor: --menu;
position-area: bottom;
margin: 4px 0 0;
padding: 8px 12px;
background: white;
border: 1px solid silver;
}
</style>
<button class="btn">Menu</button>
<div class="menu">Profile<br>Settings<br>Log out</div>| Value | Where the element ends up |
|---|---|
top | above the anchor, centered |
bottom | below the anchor, centered |
right | to the right, at mid height |
top right | in the top right corner |
bottom span-right | below the anchor, to the right of its left edge |
A popover tooltip
Anchoring goes well with the popover attribute from the lesson "Disclosure widgets and dialogs": it opens and closes the block, and CSS places it next to the button. A popover has its own default styles, so inset and margin are reset:
<style>
body { min-height: 140px; }
.info { anchor-name: --info; margin: 70px 0 0 60px; }
[popover] {
position-anchor: --info;
position-area: top;
inset: auto;
margin: 0 0 6px;
padding: 6px 10px;
border: 0;
background: steelblue;
color: white;
}
</style>
<button class="info" popovertarget="hint">What is a flat white?</button>
<div id="hint" popover>A double espresso with a thin layer of milk.</div>A popover is already positioned by the browser, so position: absolute is not needed here.
Fallback positions
If the tooltip lacks room — say, the anchor is at the very top of the window — it goes past the edge. position-try-fallbacks lists fallback options. flip-block flips the element vertically: it was above — it becomes below:
<style>
.btn { anchor-name: --top; margin: 4px 0 60px 60px; }
.tip {
position: absolute;
position-anchor: --top;
position-area: top;
position-try-fallbacks: flip-block;
padding: 4px 8px;
background: seagreen;
color: white;
}
</style>
<button class="btn">A button at the top edge</button>
<span class="tip">No room above — I am below</span>There is also flip-inline — a horizontal flip. Values can be listed with commas: the browser takes the first one where the element fits.
Support
Anchor positioning arrived in Chrome and Edge in 2024, and later in Safari. Check the state of support, especially in Firefox, on caniuse.com. Without support the element is placed by the usual position: absolute rules, so for important tooltips keep a fallback with @supports (anchor-name: --a).
Common mistake
Without position: absolute (or fixed) anchoring does not work: anchor() and position-area act only on positioned elements. A regular block simply stays in the flow:
<style>
.btn { anchor-name: --a; margin-left: 60px; }
.tip { position-anchor: --a; position-area: right; background: gold; padding: 4px; }
</style>
<button class="btn">Button</button>
<div class="tip">I did not stick — no position: absolute</div>Another cause is a typo in the name: --save and --Save are different, just like variables.