· 6 min read
CSS anchor positioning: tooltips and menus without JavaScript
CSS anchor positioning is now Baseline. Learn anchor-name, position-area, anchor() and position-try-fallbacks to place tooltips and menus with pure CSS.

CSS anchor positioning lets you place one element relative to another element anywhere on the page, using only CSS. You give the reference element a name with anchor-name, point the floating element at it with position-anchor, and then place it with position-area or the anchor() function. It became Baseline Newly available in January 2026, when Firefox 147 shipped it, so it now works in current Chrome, Edge, Firefox and Safari.
That covers tooltips, dropdown menus, popovers, inline help and "more actions" menus: the cases where you used to measure the trigger with getBoundingClientRect(), compute coordinates in JavaScript, and recompute them on every scroll and resize.
What is CSS anchor positioning?
An anchor is the element something is attached to, such as a button. The anchor-positioned element is the thing being attached, such as a tooltip. The anchor-positioned element must be absolutely or fixed positioned, and the browser keeps it attached as the page scrolls or the layout changes.
The minimal version is three declarations:
.trigger {
anchor-name: --menu-trigger;
}
.menu {
position: absolute;
position-anchor: --menu-trigger;
position-area: bottom;
}
Anchor names are dashed idents: custom identifiers that start with two dashes, like custom properties. They don't have to be unique in the document, but if several elements share a name, the browser picks one for you, so in practice keep each name to one anchor per positioned element.
The menu above sits directly below the trigger and is centred on it. There is no wrapper with position: relative, and the menu doesn't need to be a sibling or child of the button.
How does position-area work?
position-area treats the anchor as the centre cell of an imaginary 3×3 grid. The keywords pick which cell, or which run of cells, the positioned element lives in.
.tooltip { position-area: top; } /* above, centred */
.tooltip { position-area: top right; } /* above and to the right, in the corner cell */
.tooltip { position-area: bottom span-right; } /* below, starting at the anchor's left edge */
.tooltip { position-area: right; } /* to the right, vertically centred */
A single keyword implies the other: top is the same as top span-all, which means the element is above the anchor and may use the full width of all three columns while staying centred. The span- keywords are how you get the usual dropdown look, where the menu's left edge lines up with the button's left edge and extends to the right.
Logical keywords such as block-start, inline-end, start and end also work, so the same rule behaves correctly in right-to-left layouts.
One detail that surprises people: when position-area is set, auto insets and margins resolve to 0. Elements in the top layer, like popovers, normally have default margins that centre them; with position-area they don't, which is what you want. If you need a gap between anchor and tooltip, add an explicit margin.
Chromium originally shipped this property as inset-area. You may still see that name in older articles; use position-area.
When should you use the anchor() function instead?
position-area is the quick option. When you need exact edges, use anchor() inside an inset property. It returns the position of one side of the anchor:
.tooltip {
position: absolute;
position-anchor: --help;
bottom: anchor(top); /* tooltip's bottom edge at the anchor's top edge */
left: anchor(center); /* tooltip's left edge at the anchor's horizontal centre */
margin-bottom: 8px;
}
You can pass an anchor name as the first argument, anchor(--help top), if you want one element to reference more than one anchor. A companion function, anchor-size(), returns the anchor's dimensions, which is the cleanest way to make a dropdown exactly as wide as its trigger:
.menu {
min-width: anchor-size(width);
}
For centring on the anchor along one axis, the anchor-center value works with justify-self and align-self.
| Need | Use |
|---|---|
| "Put it above / below / beside" | position-area |
| Line up specific edges, offsets | anchor() in top, left, bottom, right |
| Match the anchor's width or height | anchor-size() |
| Centre on the anchor in one axis | anchor-center with justify-self / align-self |
How do you stop a tooltip going off-screen?
This is the part that used to need a positioning library. position-try-fallbacks gives the browser a list of alternative placements to try if the first one overflows its containing block, usually the viewport.
.tooltip {
position-area: top;
position-try-fallbacks: flip-block, flip-inline;
}
The built-in try tactics are:
flip-block: mirror along the block axis, so top becomes bottom.flip-inline: mirror along the inline axis, so left becomes right.flip-start: mirror diagonally, swapping the two axes.
You can combine tactics in a single option, such as flip-block flip-inline, or list plain position-area values like bottom, right. For anything more custom, define a named option with @position-try and reference it:
@position-try --below-left {
position-area: bottom span-left;
margin-top: 6px;
}
.menu {
position-area: bottom span-right;
position-try-fallbacks: --below-left, flip-block;
}
The browser uses the first option in the list that fits. If none fit, it keeps the original placement. position-try-order can tell it to prefer the option with the most available space instead of the first one that fits.
How does anchor positioning work with popovers?
The two features are designed to go together. The popover API handles showing, hiding, light dismiss and stacking in the top layer; anchor positioning handles where it appears.
<button popovertarget="actions" style="anchor-name: --actions">Actions</button>
<div id="actions" popover>
<button>Rename</button>
<button>Duplicate</button>
<button>Delete</button>
</div>
#actions {
position-anchor: --actions;
position-area: bottom span-right;
position-try-fallbacks: flip-block;
margin: 4px 0 0;
}
That is a working, keyboard-dismissable actions menu with no JavaScript. If you prefer the newer declarative attributes, HTML invoker commands open the same popover with command="toggle-popover", and the positioning code doesn't change. The same approach also applies to the picker of a customizable select, which is anchor-positioned to its button.
Can you use it in production?
Current versions of all major engines support it, but users on browsers released before early 2026 won't. Feature-detect and give those users a simple fallback:
.menu {
position: absolute;
top: 100%;
left: 0;
}
@supports (anchor-name: --a) {
.menu {
top: auto;
left: auto;
position-anchor: --menu-trigger;
position-area: bottom span-right;
position-try-fallbacks: flip-block;
}
}
The fallback only works when the menu is inside a positioned parent, which is the old way of doing it, but it keeps the UI usable. If older browsers are a large share of your traffic, a JavaScript positioning library is still a reasonable fallback, loaded only when the @supports check fails.
Key takeaways
- Name the anchor with
anchor-name: --x, attach withposition-anchor: --x, place withposition-area. - Use
anchor()for exact edges andanchor-size()to match the trigger's width. position-try-fallbacks: flip-blockhandles the "tooltip hits the edge of the screen" case without JavaScript.- Pair it with
popoverfor menus and tooltips that open, close and stack correctly. - Baseline since January 2026; wrap it in
@supportsif you need to support older browsers.
FAQ
Does the positioned element have to be inside the anchor?
No. It can be anywhere in the document, including in the top layer as a popover. It does need position: absolute or position: fixed.
What happened to inset-area?
It was the original Chromium name for position-area. The property was renamed in the spec, and position-area is the name to use.
Do I still need Floating UI or Popper?
For new code targeting current browsers, CSS covers the common cases: placement, flipping and size matching. A library still makes sense if you need to support older browsers or very custom positioning logic.