Same Anchor Name, Different Anchors: A Positioning Puzzle

CSS Anchor Positioning is shaping up to be one of the most useful additions to the platform in years. It fills a gap that has existed since the earliest days of CSS: a native way to attach one element to another. But as with any new feature, it comes with its own set of surprises. One of those surprises has been bugging me since I first ran into it, and it involves a subtle interaction between anchoring and how absolutely positioned elements find their containing block.

This particular quirk surfaced while examining a range slider demo built with Anchor Positioning and Scroll-Driven Animations. The slider has multiple anchors with the same name, each paired with its own target element. Under the normal rules of anchor positioning, that should not work — but it does. The explanation reveals a lot about how the feature actually works under the hood.

Reminder: How Anchor Positioning Works

Anchor Positioning introduces two roles: an anchor (which acts as a reference point) and a target (which is an absolutely positioned element placed relative to one or more anchors). To attach a target to an anchor in HTML, you first register the anchor with the anchor-name property, then link a target element to it with position-anchor, and finally position the target relative to the anchor with position-area.

That all works smoothly in simple cases. But things fracture when you add more anchors and targets:

Intuitively, each target attaches to its nearest matching anchor. Instead, all targets pile up at the last anchor registered in the DOM. The anchor-scope property exists specifically to control this behavior. It limits an anchor's lookup scope to a particular subtree. But in the slider demo that first caught my attention, anchor-scope is never used — yet each pair of anchors and targets lines up correctly. Hardly magic, but puzzling enough to dig into.

Duped Anchors, Working Pairs

Under typical circumstances, a target attaches to the last anchor in the DOM sharing its given anchor-name. Not, as you might expect, its closest match. So why do two (or more) anchors sharing a name coexist harmoniously in the slider?

Two words: containing block. Anchor Positioning leans heavily on how an absolutely positioned element's containing block is resolved — not as something unique to the feature itself, but precisely because targets are absolutely positioned elements. top: 0, left: 30px, and other inset properties resolve against the containing block's boundaries, yielding what is called the inset-modified containing block.

When a target is attached to an anchor, what position-area does under the hood is recompute the target's inset-modified containing block so that CSS knows exactly where an element like position-area: right should land.

Now, unless you change it, an absolutely positioned element's containing block is the viewport. Any ancestor with a position other than static becomes its containing block instead. In the slider source, each custom slider sits inside its own relatively positioned wrapper. Doing that creates a fresh containing block per slider — and, thereby, isolates each anchor-and-target pair from neighbor anchors further up or down the page.

That little CSS trick is why several repeated anchor-names in the same document can coexist without anchor-scope.

A Quieter Solution Beyond Containing Blocks

There is another way to handle repeated names — one that avoids extraneous CSS entirely. Suppose, for instance, you're building prose with margin notes anchored to different paragraphs. Wanting each note attached to its reference text implies many paragraphs with the same anchor-name. That's exactly where things go wrong.

The containing block approach fixes it by isolating each landmark. But there's an even leaner trick, what you might call the reductionist method: just use only one anchor. In a bibliography or a post's margins, your targets all sit vertically alongside their paragraphs — evenly spaced by natural column flow — so you don't need per-target anchors at all. Instead, pick a single common anchor, like the whole article body. The targets snap to that anchor, the page lays them out appropriately along the block axis, and you only move targets along the inline axis with position-area (or inset adjustments) as needed. No repeated anchor-name, no anchor-scope, no containing block juggling.

Hard Rule Choice

Since anchor-scope is (today, as of Chrome 131+) the newest related browser feature, support is limited by definition. So leaning on tools that people can use is often preferable. Understanding that anchor positioning's mechanisms — containing blocks, position-area effects on the inset-modified containing block — are what actually resolve these "extras" unlocks simpler patterns that continue working in browsers supporting anchoring since Chrome 125.