CSS anchor positioning lets you attach one element, such as a tooltip or a dropdown, to another element and keep it there without measuring coordinates in JavaScript. You name an anchor, point a positioned element at it, and the browser handles placement, including moving to the other side when there is no room. This guide shows working patterns for tooltips, popovers and dropdowns, explains how the main properties fit together, and covers the mistakes that stop it working. To try the values as you read, open the CSS Anchor Positioning Playground.
How CSS Anchor Positioning Works
Every anchored layout has the same three parts:
- Anchor element: the element you attach to, marked with
anchor-name. A button is the usual anchor. - Positioned element: the tooltip, menu or popover. It needs
position: absoluteorposition: fixedand aposition-anchorthat matches the anchor name. - Containing block: the area the positioned element is placed inside. It is also the boundary that triggers a fallback when the element would overflow.
.trigger { anchor-name: --menu-anchor; } .menu { position: absolute; position-anchor: --menu-anchor; position-area: bottom; }
Anchor names are dashed identifiers, so they start with two dashes. Older tutorials call position-area by its earlier name, inset-area, so you may meet both. Use position-area in new code.
Build a CSS Tooltip with Anchor Positioning
A tooltip needs a trigger, a tooltip element placed directly after it, and CSS that ties them together. Putting the tooltip right after the trigger lets a sibling selector show it on hover and on keyboard focus.
<button class="tip-trigger" aria-describedby="tip-1">Save</button> <div class="tip" id="tip-1" role="tooltip">Saves your changes</div>
.tip-trigger { anchor-name: --tip-anchor; } .tip { position: absolute; position-anchor: --tip-anchor; position-area: top; position-try-fallbacks: flip-block; margin: 8px; opacity: 0; pointer-events: none; } .tip-trigger:hover + .tip, .tip-trigger:focus-visible + .tip { opacity: 1; }
Try it below. The tooltip is set to appear above the button, but this button sits near the top edge of the box, so the fallback moves the tooltip below it.
display: none works too, but opacity lets you add a short fade and keeps the element in the layout so it can be measured.Position a Popover with Anchor Positioning
The Popover API pairs naturally with anchor positioning. When a popover is opened with popovertarget, the button that opens it becomes its implicit anchor, so you do not need anchor-name or position-anchor. Popovers also render in the top layer, so you do not fight z-index to keep them visible.
<button popovertarget="menu-1">Options</button> <div id="menu-1" popover> <a href="/profile/">Profile</a> <a href="/settings/">Settings</a> </div>
#menu-1 { margin: 0; inset: auto; margin-top: 8px; position-area: bottom; position-try-fallbacks: flip-block; }
inset and margin defaults. Without the reset above, position-area will not place it next to the button.position-area vs anchor() vs anchor-size()
These three tools solve different problems, and mixing them up is a common source of confusion:
- position-area places the element on one tile of a 3 by 3 grid around the anchor. It is the quickest option for tooltips and menus.
- anchor() returns an edge of the anchor and goes inside inset properties such as
topandleft. Use it when you need exact alignment. - anchor-size() returns the anchor's width or height and goes inside sizing properties. Use it to size the positioned element from the anchor.
/* position-area: pick a tile around the anchor */ .tip { position-area: top; } /* anchor(): align to exact edges */ .tip { top: anchor(bottom); left: anchor(left); } /* anchor-size(): match the anchor's width */ .menu { min-width: anchor-size(width); }
Fallbacks, Overflow and Visibility
Positioned elements get cut off when the anchor sits near an edge. position-try-fallbacks gives the browser alternative positions to try, and flip-block is the right default for most tooltips and menus because it flips above and below. Two more properties finish the job:
- position-try-order chooses between fallbacks that all fit, for example by picking the one with the most available height.
- position-visibility hides the positioned element in cases like the anchor scrolling out of view, so a tooltip does not hover over unrelated content.
.tip { position-area: top; position-try-fallbacks: flip-block; position-visibility: anchors-visible; }
For a custom fallback with its own margin or alignment, define it with @position-try. You can watch the built-in flips happen live in the playground by dragging the anchor toward an edge.
Anchor Positioning vs position: absolute vs JavaScript
| Approach | Positioned relative to | Flips at edges | Needs JS |
|---|---|---|---|
| position: absolute | Nearest positioned ancestor | No | Only to flip |
| Anchor positioning | Any named anchor | Yes, with fallbacks | No |
| Floating UI or Popper | Any reference element | Yes | Yes |
Plain absolute positioning ties the element to its ancestor, which usually forces the tooltip to live next to its target in the markup. Anchor positioning removes that coupling. A JavaScript library is still reasonable when you need browsers older than the support list below, or behavior such as arrows that follow the placement.
Why Your Anchor Positioning Is Not Working
- The positioned element is not absolute or fixed. Anchor positioning only applies to elements with
position: absoluteorposition: fixed. - The names do not match.
--tip-anchorand--tip-anchroare different names, and a missing dash pair makes the value invalid. - Several elements share one name. When a name appears on many elements, the positioned element picks the last one in source order. Give each component its own name, or generate names per instance.
- A popover is still centered. Reset
insetandmarginas shown above. - The containing block clips it. An ancestor with
overflow: hiddencan cut the element off, and fallbacks flip inside that box. - The browser lacks support. Test with
@supports (anchor-name: --a)before debugging anything else.
Using Anchor Positioning with Tailwind CSS
Arbitrary property syntax works for any CSS property, including the anchor ones. Replace spaces in values with underscores, and keep the CSS in your own stylesheet instead when the same anchor name is shared by several rules.
<button class="[anchor-name:--menu]">Menu</button> <div class="absolute [position-anchor:--menu] [position-area:bottom_span-right]">...</div>
Browser Support and Progressive Enhancement
Chrome and Edge have supported anchor positioning since version 125, Safari since version 26, and Firefox since version 147, which made it Baseline Newly available in January 2026. Check caniuse for current usage figures and the Chrome documentation for the full API. To support older browsers, write a simple static position first and layer the anchored version inside @supports:
.tip { position: absolute; top: 100%; left: 0; } @supports (anchor-name: --a) { .tip { inset: auto; position-anchor: --tip-anchor; position-area: bottom; } }
Frequently Asked Questions
What is an anchor in CSS?
In CSS anchor positioning, an anchor is any element you give an anchor-name, or the button that opens a popover. Other elements, called positioned or anchored elements, can then place and size themselves relative to it. The anchor is a layout reference, not a link.
Is a CSS anchor the same as an HTML anchor link?
No. An HTML anchor is an a element that links to another page or to an id on the same page. A CSS anchor is any element that another element uses as a positioning reference. The two share a word but solve different problems, and neither depends on the other.
How do I make a hover tooltip with CSS only?
Put the tooltip right after its trigger in the markup, give the trigger an anchor-name, and give the tooltip position: absolute, position-anchor and position-area. Hide it with opacity: 0, then show it with a :hover and :focus-visible rule on the trigger that targets the next sibling, so keyboard users get it too. Add position-try-fallbacks: flip-block so it moves when there is no room.
How do anchor() and anchor-size() differ?
anchor() returns a position, such as the bottom edge of the anchor, and is used inside inset properties like top and left. anchor-size() returns a dimension, such as the anchor's width, and is used inside sizing properties like width, min-width and max-height. Use anchor() to place an element and anchor-size() to size it, for example to make a dropdown as wide as its button.
What do position-try-order and position-visibility do?
position-try-order decides which fallback wins when more than one fits, for example by choosing the one with the most available height. position-visibility hides the positioned element in cases such as the anchor scrolling out of view, so a tooltip does not float over unrelated content.
How do I position a dropdown menu under a button with CSS?
Give the button an anchor-name, then set position: absolute, position-anchor and position-area: bottom span-right on the menu so it opens below the button and extends to the right. Add min-width: anchor-size(width) to keep it at least as wide as the button, and position-try-fallbacks: flip-block so it opens upward near the bottom of the screen.
Can I use anchor positioning in CSS today?
Yes. Chrome and Edge have supported it since version 125, Safari since version 26, and Firefox since version 147, which made it Baseline Newly available in January 2026. Check caniuse for current usage numbers, and wrap the anchored styles in @supports with a static fallback if you must support older browsers.
Experiment with anchor positioning live
Drag the anchor, pick a position area, add a fallback and copy the generated CSS in the playground.
Open the Anchor Positioning Playground