anchor()

DigitalOcean provides cloud products for every stage of your journey. Get started with $200 in free credit!

The CSS anchor() function takes an anchor element’s side and resolves to the <length> where it is positioned. It can only be used in inset properties (e.g. top, bottom, bottom, left, right, etc.), normally to place an absolute-positioned element relative to an anchor.

.target {
  position: absolute;

  top: anchor(--my-anchor bottom);
  left: anchor(--my-anchor end);
}

The anchor() function won’t have any effect on a common element on its own. That’s because it is part of the CSS Anchor Positioning module, a set of many features that work together to position an element, that we may call “target”, to another, called “anchor”.

Syntax

<anchor()> = anchor( <anchor-element>? && <anchor-side>, <length-percentage>? )

<anchor-element> = <dashed-ident>

<anchor-side> = inside | outside
  | top | left | right | bottom
  | start | end | self-start | self-end
  | <percentage> | center

Arguments

/* Explicit anchor */
top: anchor(--my-anchor bottom);
right: anchor(--MyAnchor start);

/* Physical arguments */
top: anchor(bottom);
bottom: anchor(top);
left: anchor(right);
right: anchor(left);

/* Logical side arguments */
left: anchor(inside);
top: anchor(outside);

/* Logical direction arguments */
inset-inline-start: anchor(start);
block-inline-start: anchor(end);
inset-inline-end: anchor(self-start);
block-inline-end: anchor(self-end);

/* center */
right: anchor(center);
top: anchor(center);

/* <percentage> */
right: anchor(10%)
bottom: anchor(50%); /* same as center */
right: anchor(0%); /* same as start */
bottom: anchor(100%); /* same as end */

/* Fallback */
top: anchor(--invalid-anchor bottom, 100px);
left: anchor(top, 20%);
  • <anchor-element>: Specifies which anchor element we will use to position the target.

    • <dashed-ident>: The name of the desired anchor element. The value is called a “dashed” indent because it must be prefixed with two dashes (--) exactly like we do for CSS custom properties, e.g. --my-anchor.
    • ommited: Uses the implicit anchor element defined in the position-anchor property. If there isn’t an implicit anchor, the function resolves to its fallback, otherwise it is invalid.
  • <anchor-side>: Refers to the position of a side of the anchor element.

    • <anchor-side>: Resolves to the <length> of the corresponding side of the anchor element. It has physical arguments (i.e., top, left, bottom right), logical side arguments (i.e., inside, outside) and logical direction arguments relative to the user’s writing mode (i.e., start, end, self-start, self-end) and the center argument.
    • <percentage>: Refers to the position between the start (0%) and end (100%). Values below 0% and above 100% are allowed.
  • <length-percentage>: This optional argument can be used as a fallback whenever the target doesn’t have a valid anchor or position. It positions the target to a fixed <length> or <percentage> relative to its containing block.

Positioning targets

We have two main options to position a target relative to their anchor: the position-area property and setting its inset properties with the anchor() function. While the position-area property has its quirks, it gives us a simple syntax to position the target using a 3×3 imaginary grid, so we can just say, “I want my target in the top right corner” or “I want it spanning across the bottom” of its anchor.

The anchor() function has a different approach: it chooses one side of the anchor and resolves to the <length> where it is positioned, allowing us to use it in the target’s inset properties (e.g., top, right, bottom, left, etc.) to place one side of the target next to one side of the anchor, giving us more nitpick control over the target’s position.

To start working with the anchor() function, we have to first lay out our main characters:

<div class="anchor">anchor</div>
<div class="target">target</div>

And transform them from simple div elements to anchor and target using the power of anchor positioning:

.anchor {
  anchor-name: --my-anchor;
}

.target {
  position: absolute;
  position-anchor: --my-anchor;
}

Now that they are linked together, the target can comfortably rest below its anchor, or in the case of a flex container like the following, it can completely cover its anchor.

What’s left is to move our target around using the anchor() function. To do so, we can use a motley selection of arguments.

Physical arguments

Physical arguments (top, right, bottom, left) can be used to position the target regardless of the user’s writing mode. For example, we can position the left and top inset properties of the target at the anchor(right) and anchor(bottom) sides of the anchor, effectively positioning the target at the anchor’s right-bottom corner:

.target {
  position: absolute;
  position-anchor: --my-anchor;

  left: anchor(right);
  top: anchor(bottom);
}

Since we are using the position-anchor property, the <anchor-element> argument is implicit, and we don’t need to define it again.

Be wary of physical arguments since they can only be used in the inset property of their matching axis. For example, anchor(left) can only be used on the right and left inset properties; otherwise the function is invalid and it resolves to the fallback value if one is provided. The following example is invalid because the top keyword is used on the left inset property, which does not match. A fallback value of 100px is provided in the function arguments, so that is used instead.

.target {
  position: absolute;
  position-anchor: --my-anchor;

  left: anchor(top, 100px); /* Invalid argument; falls back to 100px */
}

Using the same argument on the same left inset property won’t have any effect, but it won’t trigger the fallback either.

.target {
  position: absolute;
  position-anchor: --my-anchor;

  left: anchor(left, 100px); /* Doesn't move the target but also doesn't fallback to 100px */
}

At the same time, using physical arguments on logical inset properties (inset-block-start, inset-block-end, inset-inline-start, inset-inline-end) will only be valid if they match the same axis on the user’s writing. You can see how that can get confusing quickly, so please don’t use them together unless you have a good reason.

Logical side arguments

The logical side arguments (i.e., inside, outside), are dependent on the inset property they are in. The inside argument will choose the same side as its inset property, while the outside argument will choose the opposite. For example:

.target {
  position: absolute;
  position-anchor: --my-anchor;

  left: anchor(outside);
  /* is the same as */
  left: anchor(right);

  top: anchor(inside);
  /* is the same as */
  top: anchor(top);
}

With logical side arguments, we don’t have to worry about writing them on the incorrect axis since they always match their inset property axis.

Logical direction arguments

The logical direction arguments are dependent on two factors:

  1. The user’s writing mode: they can follow the writing mode of the containing block (start, end) or the target’s own writing mode (self-start, self-end).
  2. The inset property they are used in: they will choose the same axis of their inset property.

So for example, if we use physical inset properties in a left-to-right horizontal writing mode:

.target {
  position: absolute;
  position-anchor: --my-anchor;

  left: anchor(start);
  /* is the same as */
  left: anchor(left);

  top: anchor(end);
  /* is the same as */
  top: anchor(bottom);
}

In a right-to-left writing mode, we’d do this:

.target {
  position: absolute;
  position-anchor: --my-anchor;

  left: anchor(start);
  /* is the same as */
  left: anchor(right);

  top: anchor(end);
  /* is the same as */
  top: anchor(bottom);
}

That can quickly get confusing, so we should also use logical arguments with logical inset properties so the writing mode is respected in the first place:

.target {
  position: absolute;
  position-anchor: --my-anchor;

  inset-inline-start: anchor(end);
  inset-block-start: anchor(end);
}

<percentage> and center

Lastly, we can position our target from any point between the start and end sides, with 0% being equivalent to start and 100% to end. Since percentages are relative to the user writing mode, is preferable to use them with logical inset properties.

.target {
  position: absolute;
  position-anchor: --my-anchor;

  inset-inline-start: anchor(100%);
  /* is the same as */
  inset-inline-start: anchor(end);

  inset-block-end: anchor(0%);
  /* is the same as */
  inset-block-end: anchor(start);
}

Values smaller than 0% and bigger than 100% are accepted, so -100% will move the target towards the start and 200% towards the end.

.target {
  position: absolute;
  position-anchor: --my-anchor;

  inset-inline-start: anchor(200%);
  inset-block-end: anchor(-100%);
}

Lastly, there is also the center argument that is equivalent to 50%. You could say that it’s “immune” to direction, so there is no problem if we use it with physical or logical inset properties.

.target {
  position: absolute;
  position-anchor: --my-anchor;

  left: anchor(center);
  bottom: anchor(top);
}

Spanning targets

Using the position-area property, we could easily span a target across the anchor using the span- prefix on any value. For example, to span over two tiles over the anchor we could just set the position-area to top span-left. However, since position-area works with a 3×3 grid, we can only span the target across two or three grid tiles, so spanning the target across a smaller or bigger region would be impossible.

Using the anchor() function and percentages, we can position the target to a larger array of positions:

.target {
  position: absolute;
  position-anchor: --my-anchor;

  right: anchor(30%); /* places the right side 30% to the right of the anchor side */
  left: anchor(-100%); /* places the left side -100% to the left of the anchor side */
  bottom: anchor(top); /* places the anchor at the top */

  height: 80px; /* the width has to be unset */
}

anchor() can be used inside math functions

Since anchor() resolves to a <length>, it can be used like in math functions like calc(), clamp(), min() and max(). For example, using the calc() function, we could position our target overlapping a little over its anchor corner to make a notification element.

.target {
  --target-height: 20px;
  --target-width: 50px;

  position: absolute;
  position-anchor: --my-anchor;

  left: calc(anchor(right) - var(--target-width) / 2);
  bottom: calc(anchor(top) - var(--target-height) / 2);

  height: var(--target-height);
  width: var(--target-width);
}

Positioning a target with multiple anchors

If you just want to position an element next to another, attaching one anchor to your target is more than enough. However, if you want to visually connect two anchors that are far away, you can link them using multiple anchor() functions on the target element, each attached to a different anchor.

So for example, if we have another anchor:

<div class="anchor one">anchor one</div>
<div class="anchor two">anchor two</div>

<div class="target">target</div>

We could use the target to connect them:

.anchor.one {
  anchor-name: --anchor-one;
}

.anchor.two {
  anchor-name: --anchor-two;
}

.target {
  position: absolute;

  right: anchor(--anchor-two left);
  left: anchor(--anchor-one right);
}

Specification

The anchor() function is defined in the CSS Anchor Positioning Module Level 1 specification, which is currently in Working Draft status at the time of writing. That means a lot can change between now and when the feature becomes a formal Candidate Recommendation for implementation.

Browser support

Data on support for the css-anchor-positioning feature across the major browsers from caniuse.com

More information and tutorials