Michigan TS

CSS Anchor Positioning

Can you use it now?

@[email protected]

Good evening. I’m here to answer the question- is CSS Anchor Positioning ready to use?

No.

And the answer is no. So let’s tweak the talk a bit.

Michigan TS

CSS Anchor Positioning

Why it’s awesome and how to determine when to use it

@[email protected]

Instead, let’s talk about:

  • what anchor positioning is,
  • how it’s going to make dropdowns more performant and easier to implement.
  • Let’s talk about how it
  • simplifies existing patterns
  • and how it makes new layouts and designs possible.

And then, once I get you all excited to use it, let’s see if it’s actually ready to be used, and more generally, look at how to determine whether a web platform feature like anchor positioning is ready to be used in your project.

A rusty anchor in front of the ocean

What is Anchor Positioning?

  1. Positioning a floating element
  2. Relative to another element
Photo by Grant Durr on Unsplash

Floating elements

Removed from the normal flow of the document

A popover over a Wikipedia article.
A dropdown on Github.com

What is Anchor Positioning?

  1. Positioning a floating element
  2. Relative to another element

Relative to what?

What is my coordinate system?

A ghost hovering in a cave in a retro style video game.
Source: https://gamerant.com/stardew-valley-ectoplasm-curious-substance/

We can’t just have it hovering there without context.

Positioning provides context for the user’s actions.

Especially for floating content, positioning provides context clues to the user. If they’re taking an action, what are they taking an action on? How did they get to this menu?

position: absolute

or

position: fixed

First, we need to make our element float.

  • absolute positioned relative to its positioned ancestor
  • fixed positioned relative to the viewport
[present, debug]
An XKCD comic, with a line drawing of 
a thick bolt holding two tectonic plates together. 
The caption: Good news: Geophysicists are finally installing Earth's required anti-subduction anchor bolts.
https://xkcd.com/3078

So how do we provide the context for an element when we don’t want it relative to an ancestor or relative to the viewport? How do we do that without bolting together two elements that may need to move and flow separately, or need to be created or managed separately?

CSS anchor positioning adds a third option

[present, debug]

I’ve blurred out the good bits so we don’t get ahead of ourselves here.

CSS anchor positioning lets you position an element relative to another element. With just a few CSS declarations that we’ll be learning today, the element escapes its containing block and becomes positioned relative to a different element.

Why it was hard in CSS

  1. DOM structure
  2. Overflowing content

So, let’s try it in JavaScript

popper.js

[floating-ui.com]
js
const button = document.querySelector('#button');
const tooltip = document.querySelector('#tooltip');
computePosition(button, tooltip, {
  placement: 'right'
}).then(({x, y}) => {
  Object.assign(tooltip.style, {
    left: `${x}px`,
    top: `${y}px`,
  });
});

Why it was hard in JavaScript

  1. Unique IDs
  2. Imperative
  3. Performance
  4. Dependencies
  5. Package size

You need to tell the browser exactly what to do, not just what you want. (“I want a sandwich” vs “Get out the bread and peanut butter”)

The browser can do things with lower level code, which can make things much more performant.

If only there was a way to get the features of JavaScript with the performance and declarative syntax of CSS.

A wall anchor and screw on a board

CSS anchor positioning

That’s where CSS anchor positioning comes in, so let’s see how it works.

We already know how to make things float, using absolute or fixed positioning.

A wall anchor screwed into a board

Relative to what?

But what are we positioning things relative to?

css
.anchor { anchor-name: --my-anchor; }
.positioned { position-anchor: --my-anchor; }

First we need to make our anchor a valid anchor by giving it a name. The -- tells us this is a custom, author-provided name. This is the same as using CSS variables. CSS will never add a keyword that starts with 2 dashes. Then, we tell our positioned element to use that as our anchor using the position-anchor property.

The backside of a board with a wall anchor with a screw.

We are anchored!

[live code, present, debug]

We set the anchor-name, position-anchor. More on position-area later. Note that there is no connection in the HTML, in the content layer. All the connection exists in the presentation layer, in the CSS.

Ron Burgundy sitting behind a television anchor desk in the film Anchorman.

Explicit Implicit anchors

Go anchor yourself, San Diego.

But we don’t always have to explicitly declare a relationship.

popover

Browser support for popover

The Popover API is great match for anchor positioning.

[live code, present, debug]

popover creates an implicit anchor on the popover trigger

css
[popover]{
  margin: initial;
  padding: 0;
  border: 0;
}
Popover reset

A popover is centered by default, you will likely want to override the popover’s margin.

position-area

So how do we place this item? The spec creators realized that you almost always want to place the positioned item right next to the anchor, in some portion of the surrounding area. position-area essentially creates a 3x3 grid around the anchor, and then provides a way to easily place your element relative to that grid.

[anchor-tool.com]
css
.positioned {
  position-area: end;
}

<position-area> = [ [ left | center | right | span-left | span-right | x-start | x-end | span-x-start | span-x-end | x-self-start | x-self-end | span-x-self-start | span-x-self-end | span-all ] || [ top | center | bottom | span-top | span-bottom | y-start | y-end | span-y-start | span-y-end | y-self-start | y-self-end | span-y-self-start | span-y-self-end | span-all ] | [ block-start | center | block-end | span-block-start | span-block-end | span-all ] || [ inline-start | center | inline-end | span-inline-start | span-inline-end | span-all ] | [ self-block-start | center | self-block-end | span-self-block-start | span-self-block-end | span-all ] || [ self-inline-start | center | self-inline-end | span-self-inline-start | span-self-inline-end | span-all ] | [ start | center | end | span-start | span-end | span-all ]{1,2} | [ self-start | center | self-end | span-self-start | span-self-end | span-all ]{1,2} ]

position-area keywords

  • physical or logical
  • span-[area] = 2 columns
  • span-all = 3 columns
  • self = element’s own writing mode
[live code, present, debug]

Change the position-area value

anchor()

position-area works in most cases, but what if it doesn’t?

css
.positioned {
  position-anchor: --my-anchor;
  top: anchor(bottom);
}
css
.positioned {
  top: anchor(--my-anchor bottom);
}
css
.positioned {
  position-anchor: --my-anchor;
  top: anchor(bottom);
  left: anchor(--other-anchor left);
}

Only valid on inset properties

[live code, present, debug]
[present, debug]

You can even anchor every side on a different anchor.

Cool, but I was promised new possibilities

anchor() is a length

css
top: 1px;
left: 1rcap;
right: 50cqi;
bottom: 50dvh;
css
top: anchor(bottom); /* 131.1px */
css
top: calc(anchor(bottom) + 1rem);

anchor() is a length

But can only be used on insets

A photo layout in a very old yearbook.

Let’s talk about the design constraints of this type of layout.

  • The names are on the right of the photos, but with a bit of space.
  • The names are in the same row as their image.
  • The name is either at the top of the row, or just below the name above them.

And we have a few web-specific constraints:

  • There are 4 images in a row, but since screens are different sizes, maybe that should be dynamic?
  • The image and name should be connected for things like screen readers and interactions.
[live code, present, debug]

Handling overflow

position-try-fallbacks

Values

  • position-area values
  • flip-block, flip-inline, flip-start
  • custom @position-try rule
Position try fallbacks [live code, present, debug]
[live code, present, debug]

To recap, we placed the names where we wanted them. Then, we placed the cards relative to the names based on their writing direction. Then we used position-try-fallbacks to handle the cases where the positions overflowed. I’m not sure this is actually how I’d actually do this layout, but I wanted to incorporate Euchre.

anchor-size()

So far, we’ve been concerned with positioning, with placing an element, with setting its coordinate offset. But we can also use the size of our anchor.

css
width: anchor-size(--anchor width)
css
width: anchor-size(--anchor height)

You can refer to the opposite dimension.

css
position-anchor: --anchor;
width: anchor-size();

anchor-size() is valid for

  • sizing properties
  • insets (top, left, etc)
  • margin
[live code, present, debug]

Set the inline-size to anchor-size() and the min-inline-size to max-content

Calc with anchor-size [present, debug]
Sidenotes [present, debug]
Image Comparison [present, debug]

Should you use anchor positioning?

anchor-positioning

Browser support for anchor-positioning

How do you decide?

The web platform is constantly evolving, how do you keep up? How do you make the decision a feature is safe to use?

It works on my machine

By HumanisticRationale at English Wikipedia, CC BY 3.0, https://commons.wikimedia.org/w/index.php?curid=72904963

It’s like the adoption of Color TV. Broadcasters didn’t want to start broadcasting in color until viewers had color TVs. Viewers didn’t want to buy color TVs until there was color programming. But viewers had control over their own machines.

Your code is running on an unknowable machine.

avatar

Graphic design of unknown content with unknown collaborators, on an infinite and unknowable canvas, across operating systems, interfaces, languages, and writing modes

— Miriam Suzanne

To quote my fellow OddBird, web development is essentially…

It works on my machine

CanIUse table for justify-content space-evenly

Maybe you check out Caniuse, but what is this telling you?

Support data from CanIUse.com on the css-anchor-positioning feature
[Can I Use css-anchor-positioning]

Clearly, not available in all browsers yet so unless you’re in a Chrome-only environment, it isn’t quite ready

CanIUse summary table for grid

But tools on the web are rarely simple and straightforward. Sub grid came later. Maybe also grid-auto-rows

[github.com]

browser-compat-data tracks individual parts of the web platform

Browser support for 15,222 web api parts

BCD tracks browser support for 15k different items

When did Firefox support the hover selector for non <a> elements?

Does Safari on iPad support the ew-resize cursor if you have a connected mouse?

When did Opera add the direction attribute for the marquee element?

That’s not how developers talk about things.

Developers talk more generally. Not about a certain attribute or property or keyword but rather about features.

Bird.

For many, knowing you saw a bird is enough.

Woodpecker.

Some may be interested you saw a woodpecker.

Pileated Woodpecker.

Birders will want to know what kind of woodpecker.

Jerome.

Usually no one cares exactly which pileated woodpecker you saw.

An Rufous Hummingbird in a human hand, getting banded.

Sometimes you do need to know a specific bird. This is a Rufous Hummingbird I watched get banded. It was a ridiculously small band. Banding and tracking individual birds is similar to BCD data. It’s useful when you need it, but most of the time you can be more general.

A radar map of the U.S. showing blue circles emerging,
representing large flocks of birds flying at night.
https://birdcast.info/

You can also zoom too far out, to the point where you are only seeing things at a macro level. During migration, flocks of birds show up on radar, and scientists use that data to track patterns and let birders like me know when to take the morning off to see what birds are around. It is useful to track both large scale migration and individual birds, but neither is what you want when you are trying to identify a bird. In the same way, what’s the right granularity for knowing when you can use a web feature?

[github.com]

There’s a need for a more general way of talking about features.

Browser support for 1062 features

I was part of an effort to distill those 15000 web api parts into something closer to how developers think. It’s currently at 1062 different features.

[web-platform-dx.github.io]

Those features, along with the Browser Compat Data, power Baseline.

Feature support in

  • Chromium (Chrome/Edge)
  • Safari
  • Firefox

This does not include Node, Deno, Bun. That’s a different problem space. You control the runtime. On the web, you don’t have that control.

Available for 30 months

30 months seems to be enough time to get most people updated. It isn’t 100%, but it’s (probably) close enough. Different browsers release at different cadences, so time was more consistent than versions.

Safe to use

If it fully meets these criteria, it is considered Baseline Widely Available.

Proceed with caution
Proceed with (more) caution
A wall with an on button and an off button.

Web features and baseline boil things down to essentially a ues or no.

A control board of many switches

But that doesn’t reflect the complexity or full suite of options available.

The squishy parts

  • Can it be progressively enhanced?
  • Is the site usable without it?
  • Are there polyfills available?
  • Are some parts of the feature older?

Where can I use it?

A screenshot of the light-dark() MDN page, showing the Baseline 2024 Newly Available banner.
A screenshot of the CanIUse Anchor Positioning page, showing the limited availability banner.
A graphic comparing the CSS hover cards
in VS Code.
The Old shows a version for each browser.
The New shows the Baseline logo with
the text "Widely available across major browsers
(Baseline since 2021)"
Graphic from Rick Viscomi
A screenshot of the Baseline component, showing an example of each state.
[oddbird.github.io]

The data is open, so we have our own web component, using it for this.

grid

Browser support for grid
Web Features Explorer
[Web Features Explorer]

You can explore the features, and also see a sort of release notes for the web, that show what is newly available and widely available for use.

Connected nodes showing how web features are used.
https://patrickbrosset.com/slides/breakouts-day-2025/#interconnected-data

Does Baseline work for you?

Baseline is for a simple decision that will work in most use cases. If the 30 month works for you and for the target demographics of your product, you can take the baseline as what a group of experts recommend. Baseline Widely Available is perhaps overly cautious. If there’s a feature you want to use, and it’s “Newly Available” or “Limited Availability”, look at ways to use it without excluding users with older browsers.

So can you use anchor positioning?

anchor-positioning

Browser support for anchor-positioning

No, but…

[anchor-positioning.oddbird.net]
js
if (!("anchorName" in document.documentElement.style)) {
  import("https://unpkg.com/@oddbird/css-anchor-positioning");
}
  • Any browser
  • Support back to 2017
  • CSS Anchor Positioning API
  • JavaScript/Floating UI under the hood

It’s what you’d end up using under the hood, but with the modern API, and it won’t impact users on newer browsers.

Polyfill limitations

  • No support for dynamic elements (yet)
  • Not feature complete (yet)
  • No partial support, so some gaps in Chrome

(Fund us)

So… can you use anchor positioning?

Probably?

Definitely can play around with it, and learn it. Depending on your use case, you can use the polyfill to get this for your users now.

More from OddBird

  • We’ll build your Web Apps
  • Office Hours and Coaching
  • Resources for Learning
Create Performant Layouts & Resilient Dropdowns. 
Every week, get bite-sized lessons and demos to learn how with CSS anchor positioning.
oddbird.dev/learn/courses/anchor-positioning/
[drafts.csswg.org]
SlideDeck Controls

View:

Controls