Home / Blog / Anchor positioning not working

CSS Anchor Positioning Not Working?

Published October 2, 2026 · 11 min read · by the CSS DNA team

Quick answer: Anchor positioning fails silently. Check, in order: the positioned element has position: absolute or fixed; anchor-name and position-anchor use the same --dashed name; only one element on the page has that name (or you scoped it with anchor-scope); the anchor is visible and laid out before the positioned element; and you’re using current property names, position-area and position-try-fallbacks. For popovers positioned with anchor(), also add position-anchor: auto.

When anchor positioning breaks, the browser doesn’t log an error. The element just lands somewhere else, usually at the corner of its containing block, and you’re left guessing which of six requirements you missed. Most of them are easy once you know the list. One of them changed underneath working code in 2026.

The change most tutorials haven’t caught up with

Popovers get an implicit anchor: the button with popovertarget becomes the popover’s anchor without any anchor-name. Older tutorials and, until recently, MDN’s own popover guide show this pattern:

[popover] {
  margin: 0;
  inset: auto;
  bottom: calc(anchor(top) + 8px);
  justify-self: anchor-center;
}

That stopped working. The initial value of position-anchor used to be auto, which uses the implicit anchor. The spec changed it to normal, which behaves like auto only when position-area is set and otherwise like none. Using anchor() without position-area now means there’s no default anchor, the anchor(top) can’t resolve, and the popover falls back to its normal position. According to MDN’s compatibility data, the normal initial value arrived in Chrome 151, Firefox 151 and Safari 27; the Safari 27 release notes describe the change. An open MDN issue reports that the popover guide’s demo is broken in current browsers for exactly this reason. The fix is one line:

[popover] {
  position-anchor: auto;   /* use the invoker as the anchor */
  margin: 0;
  inset: auto;
  bottom: calc(anchor(top) + 8px);
  justify-self: anchor-center;
}

If you position with position-area instead of anchor(), you’re unaffected: normal behaves as auto there.

Browser support, checked

Every major engine ships anchor positioning now, but “supported” hides some detail. These versions come from MDN’s compatibility data and Can I Use, which puts global support at roughly 86%:

BrowserFirst versionNotes
Chrome, Edge125inset-area renamed position-area in 129; position-try-options renamed position-try-fallbacks in 128; anchor-scope from 131.
Firefox147Can I Use marks it partial: no animation of anchor() and anchor-size(), and no anchor-valid / anchor-visible for position-visibility.
Safari (macOS, iOS)26Partial in 26.x. Safari 27 adds the normal initial value, the renamed position-visibility keywords and transform-aware anchoring; Can I Use lists 27 as full support.

So “anchor positioning doesn’t work in Firefox” is out of date for current Firefox; it does, with a couple of edges missing. Firefox 145 and 146 had it behind the layout.css.anchor-positioning.enabled flag. “Doesn’t work in Safari” is true only for Safari 18 and older. For those, either let the element fall back to a sensible static position or load the OddBird polyfill. Feature-detect in CSS with @supports (anchor-name: --a) or in JavaScript with CSS.supports("anchor-name: --a").

Break the anchor

A real tooltip anchored to button A with position-area. Turn on a mistake and watch where it goes. The CSS below is exactly what is applied.

Tooltip

Every reason anchor positioning isn’t working

1. The positioned element isn’t absolutely positioned

anchor(), position-area and position-try-fallbacks apply only to elements with position: absolute or position: fixed. A tooltip left at position: relative or static ignores all of it and sits in normal flow. This is the most common one when anchor CSS gets added to an existing component that already had a position set somewhere else in the cascade.

2. The names don’t match, or aren’t dashed idents

Anchor names must start with two dashes, like custom properties: anchor-name: --menu, not anchor-name: menu. The second form is invalid and the whole declaration is dropped. A typo between anchor-name and position-anchor fails the same way, silently. Reading both computed values in DevTools is the quickest check.

3. Several elements share the same anchor name

Put anchor-name: --trigger on every card in a list and every tooltip binds to the same card. The spec says the target is the nearest ancestor with that name if there is one, otherwise the last one in DOM order, so all your tooltips stack up on the final item. Scope the name to each component:

.card         { anchor-scope: --trigger; }
.card button  { anchor-name: --trigger; }
.card .tip    { position: absolute; position-anchor: --trigger; position-area: top; }

anchor-scope limits the name’s visibility to the card’s subtree, so each tooltip finds its own button. anchor-scope: all scopes every anchor name in the subtree at once.

4. The anchor isn’t an acceptable anchor

The spec has a precise definition of an acceptable anchor element, and failing it is invisible. In practice it fails when:

  • the anchor is display: none or otherwise not rendered (MDN notes the element then positions against its nearest positioned ancestor instead);
  • the anchor is itself absolutely positioned and comes after the positioned element in the DOM, inside the same containing block, so it isn’t laid out strictly before it;
  • the anchor is inside the positioned element, which can never be laid out first;
  • the anchor is inside content skipped by content-visibility and the positioned element isn’t.

The second case shows up when both the anchor and the tooltip are absolutely positioned, say a floating action button with a label. Moving the anchor earlier in the markup fixes it.

5. You copied a property name that was renamed

Anchor positioning spent two years in flux and old articles still rank. Unknown properties are dropped without a warning, so a stale name looks exactly like a bug in your logic.

Old name in tutorialsCurrent name
inset-areaposition-area
position-try-optionsposition-try-fallbacks
position-visibility: anchors-visibleposition-visibility: anchor-visible
position-visibility: anchors-validposition-visibility: anchor-valid
an HTML anchor="id" attributeuse anchor-name, or a popover invoker for an implicit anchor

The renamed fallback keywords also changed: flip-block, flip-inline and flip-start are the current try tactics, and a position-area value such as bottom right can be used directly as a fallback.

6. A popover is fighting its own default styles

The browser’s stylesheet gives every [popover] position: fixed, inset: 0 and margin: auto, which centers it in the viewport. Set only top: anchor(bottom) and the other three insets stay at 0 while the auto margins keep centering, so the popover stretches or drifts. Reset both before positioning: margin: 0; inset: auto;. With position-area this is less visible, because insets resolve against the area cell, but the margin reset still matters if you add spacing. And remember the position-anchor: auto line from the top of this post if you use anchor().

7. The containing block is too small or clips it

An absolutely positioned element is still laid out inside its containing block, the nearest ancestor with a position other than static, a transform, or a few other triggers. Fallbacks measure overflow against that box. A tooltip inside a card with position: relative will flip to the bottom whenever it overflows the card, even with the whole viewport free, and an overflow: hidden on that card clips it. For menus and tooltips that should float over the page, use position: fixed or a popover, which lives in the top layer and escapes both problems. The z-index post has the full list of properties that quietly create these boundaries, and the sticky post covers the overflow side.

8. The anchor moves with a transform

If the anchor is animated with transform, the positioned element may follow the anchor’s untransformed layout box and stay behind while the button moves. Safari 27’s release notes call out transform-aware anchoring as new, so Safari 26 behaves the old way. If an animated anchor is part of your design, test it in each engine rather than assuming.

9. The browser doesn’t support it

Safari 18 and older, and Firefox before 147 without the flag, ignore every anchor property. Write the fallback first and enhance:

.tip { position: absolute; top: 100%; left: 0; }      /* works everywhere */

@supports (anchor-name: --a) {
  .trigger { anchor-name: --tip; }
  .tip {
    position-anchor: --tip;
    top: auto; left: auto;
    position-area: top;
    position-try-fallbacks: flip-block;
  }
}

How to debug it

Select the positioned element in DevTools and read the computed values of position, position-anchor and position-area. Then select the anchor and read anchor-name. If either name shows the initial value, the declaration was dropped, so look for a typo or a renamed property. Chrome’s Elements panel also shows an anchor badge on elements that are being used as anchors, which confirms the link exists. For the rest, this console snippet lists every element that declares a given anchor name and whether it’s rendered:

(function (name) {
  var hits = [...document.querySelectorAll("*")].filter(function (el) {
    return getComputedStyle(el).anchorName.split(",").map(s => s.trim()).includes(name);
  });
  console.table(hits.map(function (el) {
    var s = getComputedStyle(el);
    return { el: el.tagName.toLowerCase() + (el.id ? "#" + el.id : ""),
             display: s.display, position: s.position,
             rendered: el.getClientRects().length > 0 };
  }));
  if (hits.length > 1) console.warn("Duplicate name: the last one in DOM order wins unless one is an ancestor. Use anchor-scope.");
})("--trigger");

Walking computed styles for the anchor, the tooltip and every ancestor in between is slow. CSS DNA, a browser extension for inspecting CSS, lets you click an element and copy its computed CSS in one go, so you can compare the anchor and the positioned element side by side. Add CSS DNA to Chrome (free).

A tooltip that works in every current engine

<button popovertarget="tip-1">Settings</button>
<div id="tip-1" popover>Opens your account settings</div>

<style>
  #tip-1 {
    margin: 0;
    inset: auto;
    position-area: top;               /* implicit anchor: the invoker */
    position-try-fallbacks: flip-block;
    margin-block: 6px;
  }
</style>

It uses position-area, so it doesn’t depend on the position-anchor initial value, it relies on the popover invoker for the anchor, so there are no names to collide, and it lives in the top layer, so no containing block can clip it.

Frequently asked questions

Why is CSS anchor positioning not working?

The usual causes are a positioned element that isn’t position: absolute or fixed, mismatched or undashed names, several elements sharing one anchor-name, an anchor that is hidden or laid out after the positioned element, or an outdated property name such as inset-area. Popovers using anchor() also need position-anchor: auto since the initial value changed to normal.

Does anchor positioning work in Firefox?

Yes, from Firefox 147, released in January 2026. Can I Use marks Firefox as partial because it can’t animate the anchor() and anchor-size() functions and lacks the anchor-valid and anchor-visible keywords for position-visibility. Basic anchoring, position-area and position-try-fallbacks work.

Does anchor positioning work in Safari?

From Safari 26 on macOS and iOS, with partial support. Safari 27, released in September 2026, changed the position-anchor initial value to normal, added transform-aware anchoring and the renamed position-visibility keywords, and Can I Use lists it as full support. Safari 18 and earlier need a fallback or the OddBird polyfill.

What happened to position-try-options?

It was renamed position-try-fallbacks; Chrome switched in version 128. Firefox and Safari shipped only the new name. The values are the same: flip-block, flip-inline, flip-start, position-area values, or the name of an @position-try rule. The shorthand position-try sets it together with position-try-order.

Why do all my tooltips attach to the last element?

Because several elements share one anchor-name, and when there’s no matching ancestor the last one in DOM order wins. Add anchor-scope with the same name to each repeated component, so each positioned element only sees the anchor inside its own subtree.

Do I need anchor-name for a popover?

No. A popover opened by a popovertarget button uses that button as an implicit anchor. With position-area that works on its own. With anchor() you also need position-anchor: auto, and in both cases reset the popover’s default margin and inset.

Read the computed CSS, not the source

Click any element to copy the CSS that actually applied, and see a site’s fonts, colors and design system at a glance, all in your browser.

Add CSS DNA to Chrome (free)

Free: element CSS, eyedropper, ranked palette. Pro, $5/mo after a 7-day trial: exports, DESIGN.md, CSS audit. No account.