CSS Anchor Positioning Shipped Everywhere. Here's What You Can Delete.
Anchor positioning landed in all three engines. Here is the working code, the real package sizes, and the quirks nobody mentions.
Kemal Esensoy·Modified on October 17, 2026
Bundlephobia says @floating-ui/react is 90.1 kB minified, 30.8 kB gzipped. That is the package a lot of design systems pull in so a tooltip can sit above a button without falling off the screen.
I rebuilt that tooltip in CSS last month. Not on a client site, on a scratch HTML file with no build step. Fourteen lines. No JavaScript at all.
The reason this is suddenly possible is CSS anchor positioning, which Firefox 147 shipped unflagged on January 13, 2026. That was the last engine holding out. web.dev's January round-up called it Baseline Newly available the same month.
Then I went to check the Baseline dashboard before writing this, and it says Limited availability. Which is annoying, and also the most useful thing in this whole post, so let's start there.
What Actually Shipped, and When
Chrome shipped the core of anchor positioning in Chrome 125, back in May 2024. Safari followed in Safari 26. Firefox 147 turned it on by default on January 13, 2026, adding the anchor-center alignment value and position-anchor: none in the same release, per the Firefox 147 developer release notes on MDN.
Three engines, feature complete enough to announce. So web.dev wrote it up as Baseline Newly available in its January 2026 post.
But the web-features project, which is what actually computes Baseline, still lists the composite anchor-positioning feature as limited. Query api.webstatus.dev/v1/features/anchor-positioning yourself and you will see "baseline": {"status": "limited"}. There is an open issue on the web-features repo, #3558, arguing exactly this: the feature landed in all three engines while still carrying enough bugs and spec churn that flipping it to Baseline overstates how safe it is. One commenter described it as being in beta, and asked what status exists between "shipped everywhere" and "actually ready."
The bits that are unambiguously Baseline Newly available are the individual pieces: anchor-name, position-area, @position-try, and friends each have their own low date of January 13, 2026. The umbrella feature does not, because parts of it kept moving after that. position-anchor's initial value changed to normal in Chrome 151, Firefox 151 and Safari 27, which is March 2026, more than two months after the "it's Baseline" headlines.
Here is the part people skip. Newly available is not Widely available. Baseline moves a feature from Newly to Widely available 30 months after the last engine ships it. For anchor positioning that is roughly July 2028. Widely available is the threshold where you can stop thinking about it. Newly available means every current browser has it and everything older does not. If your analytics show a meaningful chunk of visitors on a Safari from 2024, anchor positioning is a progressive enhancement for you, not a foundation.
The Whole API Is Six Properties and Two Functions
You mark an element as an anchor with anchor-name: --whatever. You attach a positioned element to it with position-anchor: --whatever. You place it with position-area, which slices the space around the anchor into a nine-cell grid and lets you say top, bottom span-right, block-start span-inline-end and so on.
When the grid is too coarse, anchor() gives you the raw edges: top: anchor(bottom) means "my top edge sits on my anchor's bottom edge." anchor-size() does the same for dimensions, so width: anchor-size(width) makes a dropdown exactly as wide as the button that opened it. That one line alone replaces a chunk of Floating UI's size middleware.
Collision handling is position-try-fallbacks, a comma-separated list of alternate placements the browser tries in order when the current one overflows. flip-block, flip-inline, and flip-start cover most of it, or you define your own with the @position-try at-rule. position-try-order lets you rank fallbacks by available space instead of by list order. position-visibility hides the positioned element when its anchor scrolls out of view, which is the thing everyone hand-rolls with IntersectionObserver.
And anchor-scope keeps anchor names from leaking between repeated components, which matters the moment you render a tooltip inside a list.
That is the entire surface. Six properties, two functions, one at-rule.
A Tooltip, In Fourteen Lines
<button class="save-btn">Save</button>
<span class="save-tip" role="tooltip">Saves to your drafts</span>
.save-btn { anchor-name: --save-btn; }
.save-tip {
position: absolute;
position-anchor: --save-btn;
position-area: top;
margin-bottom: 6px;
opacity: 0;
transition: opacity .15s;
pointer-events: none;
}
.save-btn:hover + .save-tip,
.save-btn:focus-visible + .save-tip { opacity: 1; }
That is it. No computePosition, no autoUpdate, no cleanup function on unmount. The browser recomputes the position on scroll and resize because it is layout, not a subscription.
The tooltip still lives in normal document flow here, which means it can be clipped by an ancestor with overflow: hidden and it obeys the usual stacking rules. That is the old problem, and it is what the Popover API fixes.
A Dropdown That Flips When It Runs Out of Room
Anchor positioning and the Popover API are two halves of one thing, and they landed close enough together to be treated as one tool. Popover has been Baseline Newly available since January 27, 2025, when iOS Safari 18.3 completed the set. It gives you the top layer, so no z-index fights and no clipping, plus light dismiss on outside click and Escape, plus focus handling. All of that used to be somebody's 200-line hook.
<button id="acct-btn" popovertarget="acct-menu">Account</button>
<div id="acct-menu" popover>
<a href="/settings">Settings</a>
<a href="/billing">Billing</a>
</div>
#acct-btn { anchor-name: --acct-btn; }
#acct-menu {
position-anchor: --acct-btn;
position-area: bottom span-right;
margin-top: 4px;
width: anchor-size(width);
position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
position-visibility: anchors-visible;
}
The user agent stylesheet already sets position: fixed on open popovers, so you do not add it. popovertarget on the button wires up open and close with zero JavaScript. position-try-fallbacks flips the menu above the button when there is no room below, then to the other side, then both. position-visibility: anchors-visible hides the menu if you scroll the button out of the viewport while it is open.
Count the JavaScript. There isn't any.
What the Libraries Actually Weigh
Numbers from Bundlephobia, checked while writing this, all minified and minified plus gzipped:
@floating-ui/dom1.8.0: 21.3 kB, 8.2 kB gzipped@floating-ui/react: 90.1 kB, 30.8 kB gzipped@popperjs/core2.11.8: 20.0 kB, 14.5 kB gzippedtippy.js6.3.7: 42.6 kB, 14.4 kB gzipped, and it still depends on Popper on top of thatpopper.js1.16.1: 20.7 kB, 7.3 kB gzipped
I want to be careful here, because this is where these posts usually start lying. Nobody's bundle shrinks by 30.8 kB when they delete Floating UI, because tree shaking already removed the parts they never imported, and because the component library sitting on top of it is the bigger number anyway. What you actually save is somewhere between 3 and 15 kB gzipped depending on how much of the API you used.
The size is not really the point. The point is one fewer dependency in a tree you have to keep patched. Tippy pulls Popper, Popper is in maintenance, and every transitive package is a line in your lockfile that someone can compromise. I wrote about that in Your Website Is One Compromised npm Package Away From Disaster, and it is the same argument as the CSS one: things you deleted cannot break. Same shape as Every JavaScript Animation Library You Can Replace With 3 Lines of CSS and 10 Figma Plugins You Don't Need If You Just Learn These CSS Tricks. The platform keeps absorbing the layer above it, and most teams find out two years late.
Where the Libraries Still Win
Virtual elements. Floating UI can anchor to a plain rectangle you invent: a mouse coordinate, a text selection range, a spot on a canvas. CSS anchor positioning needs an actual DOM element with anchor-name on it. Context menus that follow the cursor, selection toolbars, chart tooltips tracking a data point, none of those have an element to anchor to. Keep the library.
Hover triggers on popovers. The Popover API opens on click. There is no declarative hover trigger with cross-browser support: the interestfor attribute that would give you one is Chrome 142 and up, with no Firefox or Safari support at all. The :hover example above works because that tooltip is not a popover. The moment you want a hover-triggered element in the top layer, you are back to JavaScript.
Complex collision strategies. position-try-fallbacks gives you an ordered list of discrete placements. Floating UI's shift middleware slides an element continuously along an axis to keep it in view, which is a different behaviour and often the nicer one. There is no CSS equivalent.
Framework integration. @floating-ui/react is 30.8 kB gzipped mostly because it is not a positioning library, it is a positioning library plus focus management, roving tabindex, typeahead, dismiss handling and ARIA wiring. Popover covers some of that. Not all of it.
The Quirks Nobody Puts in the Blog Posts
Web Platform Tests pass rates on stable, from the webstatus.dev API: Chrome 93.9%, Safari 87.0%, Firefox 84.0%. Chrome on Android is 67.9%. That last number is the one I would stare at, because on most sites I look at, mobile Chrome is the single largest slice of traffic. "It works in Chrome" and "it works in Chrome on a phone" are not the same claim here.
Specific things that bit me or that the compat data warns about:
Single-keyword position-area values are newer than the feature. top, bottom, left, right, block-start and block-end as standalone values only landed in Chrome 144 and Firefox 148, in early 2026. The span-* forms go back to Chrome 129 and Firefox 147. So the tooltip example above needs Firefox 148, not 147, even though anchor positioning "shipped" in 147.
position-visibility values got renamed mid-flight. anchors-valid is in Firefox 147 and Safari 26.2 but not Chrome. Safari 27 renamed it to anchor-valid and anchor-visible, which Chrome and Firefox do not have. anchors-visible and no-overflow are the two values supported across all three. Use those.
position-try-order is Firefox 148 and up. Not 147.
The HTML anchor attribute does not exist yet. It is behind a flag in Chrome preview only. Use anchor-name and position-anchor in CSS.
anchor() is not transitionable in Firefox. Chrome and Safari can animate it. Firefox cannot.
None of these are dealbreakers. They are the reason I would not describe this feature as done, and the reason the Baseline dashboard is arguably right and the headline was arguably early.
The Pattern That Makes This Safe to Ship
Write the boring version first, then upgrade it. @supports on a made-up custom property name is the test:
.save-tip {
position: absolute;
top: 100%;
left: 0;
margin-top: 6px;
}
@supports (anchor-name: --x) {
.save-btn { anchor-name: --save-btn; }
.save-tip {
position-anchor: --save-btn;
position-area: top;
top: auto;
left: auto;
margin: 0 0 6px;
position-try-fallbacks: flip-block;
}
}
Old browsers get a tooltip that is always below the button and occasionally clipped. New browsers get one that flips. Nobody gets a broken layout, and nobody downloads 8 kB of JavaScript to find out which group they are in.
Oddbird maintains a polyfill if you need the real behaviour on older browsers. Just be clear about what you are doing: you are adding JavaScript back to emulate the CSS that was supposed to let you delete JavaScript. For a tooltip, that trade is not worth it. Ship the @supports version instead.
What I Would Actually Do Today
If you have Tippy or Popper in a project, delete them. Popper is in maintenance, Tippy drags Popper along, and the CSS plus Popover combination covers the click-triggered dropdown and the hover tooltip that make up most of their usage. That is a real cleanup, on the same list as everything in 10 Things You Can Delete From Your Website Today.
If you have Floating UI, look at what you use it for before touching anything. Anchored to real elements, simple flip behaviour, no virtual elements: you can drop it. Cursor-following menus, selection toolbars, canvas tooltips, or you are deep in @floating-ui/react's interaction hooks: keep it, and revisit in 2027.
And if you are choosing for a new build, this is the same calculation as picking a stack that does less by default, which is most of why Astro sites pass Core Web Vitals more often than WordPress ones. Fewer moving parts, fewer things to keep current.
I do not think anchor positioning is finished. Chrome Android at 67.9% on the test suite says it is not, and the Baseline dashboard saying "limited" while the blog posts say "Baseline" is a fair summary of where it sits in September 2026. But behind @supports, with the boring fallback underneath, there is no reason to wait. The worst case is that some visitors get a tooltip in a slightly duller position, and you shipped less code.
If you want someone to go through your dependency tree and tell you what the platform has quietly made redundant, that is a normal week at Wunderlandmedia. Bring your lockfile.
Find these posts useful? Mark Wunderlandmedia as a preferred source on Google â my articles will then show up more often in your Search results, AI Overviews and AI Mode.
Set as preferred sourceAbout the Author
Kemal Esensoy
Kemal Esensoy, founder of Wunderlandmedia, started his journey as a freelance web developer and designer. He conducted web design courses with over 3,000 students. Today, he leads an award-winning full-stack agency specializing in web development, SEO, and digital marketing.