Anchor Positioning Is Disruptive
New layouts will be possible
The more I play with it, the more convinced I am that anchor positioning is going to unlock some surprising new layouts.
How to find an anchor element
It is frustrating to track down why an anchor isn’t being found. I’ve found a simple way that should work in most cases. If that doesn’t work, step through the checklist, and then dive in to get a better understanding of how Anchor Positioning works.
TL;DR – For the best chance of having anchor positioning work, here’s my recommendation:
Go give that a try, and then come back and find out what to check next if that didn’t work.
I’ll wait.
Anchor positioning has a ton of
possibilities, and is fun to play around
with. But sometimes things start to break. The positioned element can’t find the
anchor, it isn’t positioned correctly, and Dev Tools just says --anchor is not defined
.
Then you need to figure out why… Is it related to how you’ve structured your markup, a browser bug or partial implementation, or maybe it’s how you’re using Shadow DOM?
There are many reasons why it can fail, but they all fail in the same way. This makes it really hard to troubleshoot and recover from.
Here’s how to check for some of the common reasons why an anchor isn’t found:
anchor-scope
subtree.anchor-name
is defined by styles in the same shadow or document tree
where it is referred to.If your issue isn’t on this list, let us know how you fixed it! This list isn’t exhaustive, and omits some cases with hidden content, fixed position anchors, and other less likely edge cases.
We’re also available for office hours to help work through your specific case.
The examples in this article are best viewed in a Chromium browser, version 131
or later. In the examples that show that an anchor is not found, be aware of a
bug in Chromium that causes
elements with a position-area
rule that do not have a valid anchor to be
positioned incorrectly. 🤷🏼♂️
While a positioned element can be a child of the anchor, this is the primary place where I’ve seen anchor positioning fail.
The spec has specific requirements regarding the relationship between the containing blocks of the anchor and the positioned element – and containing blocks are essentially invisible, except in their effects, to developers. This leads to unexpected and surprising behavior.
While containing blocks deserve an entire deep dive post, in the meantime there’s a brief note at the end of this article.
The containing block for the positioned element cannot be a descendant of the containing block for the anchor. Put a different way, the space in which the positioned element can be positioned cannot be smaller than the space in which the anchor can be positioned. (Note: this is technically less accurate, but it helps me visualize the rule.)
Add this to your mental model:
The space in which the positioned element can be positioned cannot be smaller than the space in which the anchor can be positioned.
Crucially, if the anchor element is a parent to the positioned element and creates a containing box for the positioned element, anchor positioning will not work. The positioned element’s containing box will be the anchor, and the anchor’s containing box will be one of its ancestors.
See the Pen Absolute anchor order by @jamessw on CodePen.
There are many things that can cause the anchor element to create a containing block, and positioned elements that are children will not work:
position
besides static
on the anchor and the positioned
element is position: absolute
transform
, translate
,
scale
, etc.This is not an exhaustive list. Because there are so many ways to get into this situation unexpectedly, I recommend not nesting positioned elements inside the anchor.
Most anchors will be elements, but if you’re using a pseudo-element, not all
qualify. The pseudo-element must be a “fully styleable tree-abiding
pseudo-element.” Tree-abiding pseudo-elements behave like regular elements,
unlike pseudo-elements like ::first-letter
or ::spelling-error
. Some, like
::marker
or ::placeholder
, are not fully styleable, as they only allow some
CSS properties.
The valid pseudo-elements are ::before
, ::after
and
::file-selector-button
. ::-webkit-slider-thumb
currently works as an anchor
in Chrome, but as it is experimental and not part of any CSS spec, it’s unclear
whether it should.
See the Pen Valid anchor pseudo-elements by @jamessw on CodePen.
Anchor scope is great for making reusable anchoring rules, especially if you are
anchoring on a list item or reusing styles. If you’re using anchor-scope
,
verify that both the anchor and positioned element are descendants of the
element with the anchor-scope
rule, or if the anchor itself has the
anchor-scope
rule, that the positioned element is a descendant of the anchor.
See the Pen Anchor Scope by @jamessw on CodePen.
This is the motivation behind the recommended solution to have the anchor come before the positioned element in the DOM.
Generally, absolutely positioned elements are rendered after relatively positioned elements. If the anchor element is absolutely positioned, then the positioned element must come after the anchor in the DOM.
See the Pen Absolute anchor order by @jamessw on CodePen.
The “after the anchor in the DOM” check happens on the flat tree
, which means
that it happens after slotted content is placed and shadow hosts are filled with
their children.
If you are using dialogs as modals or popovers, you are creating top layers. If the anchor element is in a higher top layer than the positioned element, the positioned element will not be able to locate the anchor.
You can position the root popover or dialog directly using position: absolute
.
See the Pen Top Layer by @jamessw on CodePen.
However, if you want to position an element that is inside the popover or
dialog, you will need to use position: fixed
. Note that the positioned
elements are not inside their parents in this example – position: fixed
moves
the element’s containing block to the viewport and allows positioning to work.
See the Pen Top Layer - inside by @jamessw on CodePen.
An element in one tree can anchor to an element in another tree, as long as the
relevant styles are all defined in the same style tree. In other words, if
anchor-name
is defined in a shadow tree, the position-anchor
or anchor()
styles must also be defined in that shadow tree. If the anchor-name
is defined
outside a shadow tree using ::part()
, then the position-anchor
or anchor()
styles can be defined outside as well.
See the Pen Anchor on Shadow Part by @jamessw on CodePen.
Notably, a containing block is not a box (it is a rectangle)…
Great, I totally understand…
You likely have run into containing blocks before. When you are positioning something with absolute positioning, it is positioned relative to its containing block.
See the Pen Containing box for absolutely positioned element by @jamessw on CodePen.
If you use percentages to define widths and heights, these are calculated relative to the element’s containing block.
To figure out an element’s containing block, find the ancestor element that the
element’s position and size are relative to. This is dependent on the element’s
position
value, so for example, if the element is fixed position, the
containing block can be the viewport, or if the element is relative position,
the containing block could be generated by an ancestor <li>
element.
I’ve found MDN’s guide on Identifying the containing block a helpful resource to unravel the containing block.
Troubleshooting why an anchor is not found is not fun or easy. Anchor Positioning is in its early days, but as adoption grows, I hope we can find ways to make this easier.
An important part will be to improve dev tooling when an anchor is not found.
Perhaps next to a --anchor is not found
message, there could be a crosshair
selector to select the DOM element you thought would be the anchor. Then the Dev
Tools could provide a specific message of why that particular combination would
not work.
Another useful improvement would be a method to identify an element’s containing
block. Perhaps there could be a new :containing-block
pseudo-class that
selects the element that creates the containing block, or a
HTMLElement.containingBlock
attribute. Because this is primarily useful while
developing, it may be better to instead add a way of finding this in Dev Tools,
instead of through browser APIs.
I think we also need to find better mental models to understand render order and containing blocks. Is there a way we could move this from a set of guidelines and a checklist of gotchas to avoid, to a place where these rules click and make sense to developers?
If you found this article helpful, please sponsor our work! Deep dives like this take time and energy, and we want to keep them coming!
You can also hire us to develop the Anchor Positioning polyfill or another OSS language/tool you rely on. Our client work also helps fund our educational work like this article, so get in touch with us if you have any web development needs.
A huge thank you to the individuals and organizations sponsoring OddBird’s open source work!
We love contributing back to the languages & tools that developers rely on, from CSS & Sass to browser polyfills and Python. Help us keep that work sustainable and focused on developer needs!
New layouts will be possible
The more I play with it, the more convinced I am that anchor positioning is going to unlock some surprising new layouts.
Performance, scope, and fallbacks for the anchor positioning polyfill
Our sponsors are supporting the continued development of the CSS Anchor Positioning Polyfill. Here’s a summary of the latest updates.
Are we measuring what we meant to measure?
There’s been a lot of interest in the results of the annual State of CSS survey, but are we asking all the right questions?