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 theposition-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 thecenter
argument.<percentage>
: Refers to the position between thestart
(0%
) andend
(100%
). Values below0%
and above100%
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:
- 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
). - 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
More information and tutorials
- CSS Anchor Positioning in Practice – Winging It Live (YouTube) (Miriam Suzanne & James Stuckey Weber)
- “Introducing the CSS anchor positioning API” (Una Kravets)
- “Drawing a Line to Connect Elements with CSS Anchor Positioning” (Silvestar Bistrović)
Related
anchor-size()
.target { width: anchor-size(width); }
anchor-name
.anchor { anchor-name: --my-anchor; }
position-anchor
.target { position-anchor: --my-anchor; }
position-area
.target { position-area: bottom end; }
position-try-fallbacks
.target { position-try-fallbacks: flip-inline, bottom left; }
position-try-order
.element { position-try-order: most-width; }
position-visibility
.target { position-visibility: no-overflow; }