Dec 14

Anchor Positioning

Anchor Positioning is an incoming dynamic addition to enhance the capabilities of CSS positioning.

By Roman Komarov

Imagine CSS Flexbox without the ability to use the flex property. Imagine CSS Grid without an ability to use repeat() for its templates, or an ability to configure grid-column and grid-row for specific elements. Without these essential features, these layout methods will not make much sense.

An illustration showing a big, slightly rotated anchor emoji on a black background with a green bubble pointing to it. The bubble contains the text “Hello!”. There are two light-blue dashed lines, one vertical and one horizontal, intersecting the point where the anchor and bubble touch. Above the horizontal line, there is an “inset-inline = ?” text, and on a side of the vertical one there is “inset-block = ?”. In the bottom-right section of the illustration, the text “Anchor Positioning” is written in a bigger font over two slightly rotated lines. An icon of a paper airplane with a dashed line after it is placed on the right of the word “Anchor”, as if launched by it.
Illustration source

In a few years, after anchor positioning will become available in all browsers, we will look back at it and think: “How could we use it without anchors?” — that’s what I can say now, after experimenting with today’s anchor positioning prototypes and thinking about its specs.

The Disclaimer

The first thing that needs to be said before discussing anchor positioning is — its current status.

Today, we cannot use anchor positioning in any regard. It is not ready.

The specification is evolving and improving; it is not done yet. While the first Working Draft was published in June, since then, there have been developments (see the Area-based Positioning section), which resulted in many additions to the current Editor’s Draft. Furthermore, there are close to 50 open issues, and it is uncertain what the specification will include when it will be ready.

That said, even today, there is a lot to play with!

You might want to open the examples you will see in the latest Chrome Canary with the “experimental web platform features” feature flag turned on so you can play with them yourself!

In this post, I won’t provide a “tutorial” on how to use anchor positioning. It is a large specification, and as it is not finalized, any tutorial will soon be outdated. If you’d want to read something besides the specification, I recommend the introductory article by Jhey Tompson. However, many of its details are outdated, like the change from anchor-scroll to anchor-default and several other aspects.

My goal today is to talk about what anchor positioning brings to the table, not to get lost in the particularities of the current implementation. I will not include many demos and examples of code in this post but will link to a list of experiments for you to explore in the end.

The Concepts

In a general sense, anchor positioning is an addition to the existing absolute and fixed positioning (I’ll use “absolute” for both in the future to simplify things). It augments them by providing the superpower of knowing the positions and dimensions of other elements.

By itself, absolute positioning is absolute: you give it coordinates that are based on its positioning context (closest positioned ancestor or its initial containing block). That’s one of the two things our element knows about. The other is a “hypothetical box” — the initial position of the element if an inset property was not set, which might be a powerful way to handle absolute positioning but a fragile one.

And that’s it. Every so often, this is enough, but for many cases where we want an element to be visually attached to something, these limitations result in either us adding “magic” numbers or a lot of JavaScript.

Anchor positioning helps us by providing the information we need — and sometimes more.

Connecting Elements

Anchor positioning does this by allowing us to explicitly connect an absolutely-positioned element to another element.

We do this in two steps in CSS:

  1. We give our anchor element the anchor-name property, which can contain one or more dashed identifiers, for example, anchor-name: --tooltip-context.
  2. At the same time, we use this name on our absolutely-positioned element, mentioning it in the anchor-default property, or inside the anchor() or anchor-size() functions.

When we do this, we connect our anchored element with its anchor, providing the coordinates and dimensions to be used as the base for positioning. That means our elements now do not live in isolation — they are connected, and a change in the size or position of one would result in the corresponding change in another.

In the below example, we can see how when we click on the button, the popover appears attached to it.

See the CodePen.

We’re not limited by connecting one element to one another. We can have a many-to-many relationship: multiple elements can connect to one anchor, and one element can use the information from multiple anchors, including inside calculations!

The demo above, for example, uses multiple anchors to position the arrow of a popover. You might notice that the popover itself is centered relative to the button, but the arrow has two attachment points: the popover itself and a snowflake inside the button.

Speaking of centering behavior, this is the most complex part of this demo. The code for it might look unappealing:

[popover] {
inset-inline: calc(
anchor(50%) -
min(
abs(0% - anchor(50%)),
abs(100% - anchor(50%))
)
);
margin-inline: auto;
}

Try wrapping your head around how this is calculated! This code is an adapted version of what was in the specs initially, but thankfully, we will have better ways of handling this in the works.

Area-based Positioning

This summer, Elika J. Etemad and Jen Simmons from Apple, with the help of Miriam Suzanne, provided their alternative exploration of anchor positioning. You can read their original exploration and go through the slides they showed at the CSSWG face-to-face meeting.

Many things were proposed in the exploration, but the first thing that found its place in the revised specifications is the inset-area property — the idea of positioning things using a grid-like syntax.

As I demonstrated in the above example, we had to use overcomplicated calculations to center things properly. In the future, we will have an anchor-center value for self-alignment properties, but even then, we will have to rely on specifying the inset properties, which might be cumbersome.

The way inset-area would help with this is by allowing us to tell the anchored element to be positioned in a specific area of a 3×3 grid, which is formed around its positioning context and its anchor.

An illustration showing a 3 by 3 grid. There is a yellow, rounded block with the “anchor” text in the middle of this grid, and a rounded magenta block with the text “1st choice” takes two cells horizontally and is placed in the top-left corner. Around the grid there are multiple headings which label each row and column. On top the columns are: “start”, “center” and “end” (in green). On the bottom the same columns are labeled as “left”, “center” and “right” (in blue). On the left the rows are: “start”, “center” and “end” (in green), with the corresponding labels on the other side “top”, “center” and “bottom” (in blue). Both on the top and on the left of the illustration, there are dark blue lines that span the whole width and height of the grid, with the label “all” near them, in dark blue color.
An example of inset-area: start center / top positioning. This illustration first appeared in the slides, now present in the specs.

This is intended to provide a simple way to handle the most common cases for popovers and tooltips and will come with several built-in mechanisms for how the alignment will be handled.

I did file a CSSWG issue suggesting that we could use another anchor as a way to define our “containing block” that is used for inset-area.

While I was writing this article, the Chromium team started working on the inset-area prototype, and right now, we can play with it in Chrome Canary.

However, not all the moving pieces are in place that could help us see how useful this method will be. One of inset-area’s prominent features is how it will work with self-alignment properties, and this interaction is not yet in the prototype.

A few months back, after Apple presented their ideas, I created a prototype using existing anchor positioning in Chrome Canary. It used overly complicated CSS based on cyclic toggles as an attempt to test this syntax alongside the self-alignment and the potential ::tether pseudo-element.

See the CodePen.

It does not do everything right and might differ from what we have in Chrome Canary for inset-area. Nevertheless, I got enough insights from it, and I invite you to play with it and make your own observations! (Don’t look into the source — it is messy. I warned you!)

Position Fallbacks

Another big aspect of anchor positioning is how it will handle how our anchored elements behave when they go beyond their scrollports.

There are multiple ways to achieve this in the current Canary implementation, but the specification is in motion, and it is certain that things will change, so I won’t describe the way it works now.

The idea that will stay: we will have a way to define how things will look when there is no space for an element, like flipping the popover to a different place.

In this screen recording of the demo in action, the popover changes its position when there is not enough space. The way the inline inset is set up also allows it to shrink to remain centered when located at the edge of the screen.

This is a video from a demo I did show before, where I used the current implementation and some hacks to achieve the desired behavior. Hopefully, the final specification allows us to do things without hacks!

Accessibility Considerations

Apart from the anchor positioning not being ready for production in any way, one thing is worth mentioning: anchor positioning is used for connecting things with each other.

When using the popover attribute, the connection would be established, however, with an anchor attribute by itself, and when done in CSS via anchor-name, this connection is purely visual.

That means that while it is possible to connect elements that are located in different places on the page, anchor positioning could lead to the content not being accessible: visually, the content would be located together, but for keyboard navigation or any assistive technologies the pieces will be disjointed.

Again, the specs and its prototype implementation are in motion, and nothing is certain yet. We need to explore what anchor positioning could mean for accessibility in the future and participate in the discussions about that.

For example, I created an issue in CSSWG, where I proposed to automatically adjust the elements' logical (tab and reading) order when they’re connected via anchor positioning. It would be great if this could be handled from the get-go, and if you have anything to add — add your voice!

There were many aspects of anchor positioning that I did not cover in this post: anchor-size() (which I wish could be used for non-sizing properties), how to take the scroll into account, and other aspects of the features I covered or mentioned (for example, the nuances of how fixed positioning works when anchored). This is a complex spec!

In closing, I will list several articles and demos I recommend you check out if you are keen to learn more and see various use cases where anchor positioning can help. There are plenty of things we could do with it!

If you’re curious how things evolved, you can look at all the explainers that were done:

Roman Komarov

Roman Komarov

Roman have been writing CSS as a full-time job for more than 16 years, always experimenting and pushing the boundaries of what is possible, trying the new specifications and giving his feedback as an author.

Roman selected QueerJS for an honorary donation of $50

QueerJS

QueerJS is a meetup series where everyone is encouraged to attend and support the speakers and the idea. If you're queer and want to speak, this meetup is for you! It exists to give you a voice and to make a safe space where everyone is welcome.