Run this yourself. The script that produced the bundle figures below:
anchor-positioning-bundle-cost/in the Coding Dunia code-examples repo.
CSS anchor positioning is a set of CSS properties, anchor-name, position-anchor, and the anchor() function, that tie one element's position to another element's rendered location without any JavaScript calculation. Every tooltip and dropdown library exists to solve one problem: keep element B positioned relative to element A, correctly, even as the page scrolls, resizes, or the anchor moves. That's a surprisingly involved calculation once you account for viewport edges, and it's exactly the calculation CSS anchor positioning now does natively.
How Do You Link an Anchor to a Tooltip?
.trigger-button {
anchor-name: --my-tooltip-anchor;
}
.tooltip {
position: fixed;
position-anchor: --my-tooltip-anchor;
top: anchor(bottom);
left: anchor(center);
translate: -50% 8px;
}
<button class="trigger-button">Hover me</button>
<div class="tooltip" popover>Helpful tooltip text</div>
anchor-name gives the button a named anchor reference. position-anchor on the tooltip points at that name, and anchor(bottom)/anchor(center) compute the tooltip's top/left relative to the anchor's actual rendered position, bottom edge and horizontal center respectively. This recalculates automatically on scroll and resize, the same behavior Floating UI's autoUpdate() exists to provide manually. Per MDN, the anchor name itself is a dashed-ident, the same custom-property-style syntax CSS variables use, which means one anchor element can be referenced by several positioned elements at once, a single button anchoring both a tooltip and a badge, for example, without any extra JavaScript wiring.
How Do Fallback Positions Work With @position-try?
.tooltip {
position: fixed;
position-anchor: --my-tooltip-anchor;
top: anchor(bottom);
left: anchor(center);
translate: -50% 8px;
position-try-fallbacks: flip-block;
}
@position-try flip-block {
top: anchor(top);
bottom: unset;
translate: -50% -8px;
}
position-try-fallbacks: flip-block tells the browser: if the tooltip would overflow the viewport in its preferred position (below the anchor), try the flip-block fallback instead (above the anchor). Several built-in try-tactic keywords exist (flip-block, flip-inline, flip-start) covering the common flip cases without a custom @position-try block at all, worth checking before writing a manual fallback. Per the spec, you can also list multiple fallbacks in priority order, position-try-fallbacks: flip-block, flip-inline;, and the browser evaluates them one at a time until it finds a position that fits, which is the same collision-avoidance logic Floating UI's autoPlacement() middleware implements in roughly two hundred lines of JavaScript.
How Do You Combine Anchor Positioning With popover?
<button popovertarget="user-menu" class="menu-trigger">Account</button>
<div id="user-menu" popover class="dropdown-menu">
<a href="/profile/">Profile</a>
<a href="/settings/">Settings</a>
<a href="/logout/">Log out</a>
</div>
.menu-trigger {
anchor-name: --user-menu-anchor;
}
.dropdown-menu {
position-anchor: --user-menu-anchor;
top: anchor(bottom);
right: anchor(right);
margin-top: 4px;
}
The popover attribute is an HTML global attribute that gives any element top-layer rendering, meaning it paints above the rest of the page regardless of stacking context, along with built-in light-dismiss and keyboard accessibility, all without JavaScript. popovertarget on the button wires up open/close behavior, focus handling, and light-dismiss (clicking outside closes it) entirely through HTML attributes, no JavaScript addEventListener needed for that part either. anchor-name/position-anchor handle where it appears. Together, this is a complete dropdown menu, positioning, top-layer rendering, dismiss behavior, and keyboard accessibility, without a single line of JavaScript or a dependency, replacing what used to take two or three separate npm packages working in concert.
How Do You Size the Positioned Element to Fit?
.dropdown-menu {
position-anchor: --user-menu-anchor;
top: anchor(bottom);
right: anchor(right);
max-height: anchor-size(height); /* size relative to the anchor's own height, if useful */
max-width: 320px;
overflow-y: auto;
}
anchor-size() reads a dimension from the anchor element itself, useful for a dropdown that should match its trigger's width exactly (width: anchor-size(width);), rather than having a fixed or content-based width unrelated to the button that opened it. According to Chrome for Developers, anchor-size() accepts width, height, inline-size, block-size, and both self and inline dimensions, so a dropdown can also cap its own height at the viewport space actually available near the anchor, not just match the trigger's width.
What Is the Browser Support and Fallback Story?
Anchor positioning shipped in Chrome and Edge in 2024, with Firefox and Safari following through 2025 and into 2026. For the remaining gap, this is a feature where a JavaScript fallback still matters more than most CSS features covered elsewhere in this series, an unsupported browser doesn't gracefully degrade to "no positioning," it renders the tooltip at its default static position, likely overlapping other content. Feature-detect with @supports (anchor-name: --a) and load Floating UI conditionally only for browsers that fail that check, rather than assuming universal support the way you reasonably could with :has() or scroll-driven animations by 2026.
A quick three-step check before shipping anchor positioning without a fallback:
- Confirm your actual analytics show the browser mix you're targeting, don't assume from caniuse alone.
- Wrap the CSS in
@supports (anchor-name: --a)so unsupported browsers can be routed to a JavaScript fallback instead of silently rendering overlapping content. - Load Floating UI or a similar library conditionally, only inside the unsupported branch, so supported browsers never pay for the extra dependency.
Anchor positioning is the rare 2026 CSS feature where an unsupported browser does not gracefully degrade, it renders the positioned element at its default static location, likely overlapping other content, which is why feature-detecting with @supports before dropping a JavaScript fallback still matters here.
How Does This Compare to the Old Approach?
Four common jobs, tooltip attachment, scroll recalculation, viewport-overflow handling, and dropdown dismiss behavior, used to each need their own piece of JavaScript. The table below lines up what each one cost before against what it costs with anchor positioning and popover combined.
| Task | Before | With anchor positioning |
|---|---|---|
| Tooltip stays attached to its trigger | Floating UI / Popper.js, JS position calc | anchor-name + anchor() |
| Recalculate on scroll/resize | Manual autoUpdate() listener | Automatic, native |
| Flip to avoid viewport overflow | Manual collision detection logic | position-try-fallbacks |
| Dropdown open/close, focus, dismiss | Custom JS or a UI library | HTML popover attribute |
So What Does That Dependency Actually Weigh?
"Removes a dependency" is the kind of claim that sounds like a win until someone asks how big the dependency was. So I weighed it. Not computePosition on its own, which would flatter the CSS side unfairly, but the tooltip nobody would ship without: offset for the gap, flip to move above the trigger when there's no room below, shift to keep it inside the viewport, arrow for the triangle. Bundled with esbuild, minified, then compressed.
@floating-ui/dom 1.8.0, tooltip build | Bytes | |
|---|---|---|
| minified | 15,098 B | 14.74 kB |
| minified + gzip | 6,115 B | 5.97 kB |
| minified + brotli | 5,529 B | 5.40 kB |
Measured 2026-08-24 on Node 20.20.0, esbuild 0.28.2. The CSS version ships 0 B of JavaScript.
Six kilobytes gzipped isn't going to sink your page, and I'd rather say that than pretend otherwise. Tree-shaking also means your figure moves with your middleware list, so a tooltip that never flips comes in lighter than this one. The reason to reach for anchor() isn't the six kilobytes, it's that a dependency you deleted has no upgrade path, no breaking change in its next major, and no autoUpdate() listener you have to remember to tear down.
What Should You Take Away From This?
Anchor positioning doesn't add a capability CSS never had a workaround for, it removes the JavaScript dependency that workaround required. For a straightforward tooltip or dropdown, anchor-name/anchor() combined with the popover attribute covers what Floating UI existed to solve, with the caveat that support isn't universal enough yet to drop the JavaScript fallback for every project without checking your actual browser support requirements first.