CSS Anchor Positioning

W3C Working Draft,

More details about this document
This version:
https://www.w3.org/TR/2025/WD-css-anchor-position-1-20251007/
Latest published version:
https://www.w3.org/TR/css-anchor-position-1/
Editor's Draft:
https://drafts.csswg.org/css-anchor-position-1/
History:
https://www.w3.org/standards/history/css-anchor-position-1/
Feedback:
CSSWG Issues Repository
Inline In Spec
Editors:
Tab Atkins-Bittner (Google)
Elika J. Etemad / fantasai (Apple)
Ian Kilpatrick (Google)
Former Editor:
Jhey Tompkins (Google)
Suggest an Edit for this Spec:
GitHub Editor

Abstract

This specification defines anchor positioning, where a positioned element can size and position itself relative to one or more “anchor elements” elsewhere on the page.

CSS is a language for describing the rendering of structured documents (such as HTML and XML) on screen, on paper, etc.

Status of this document

This section describes the status of this document at the time of its publication. A list of current W3C publications and the latest revision of this technical report can be found in the W3C standards and drafts index.

This document was published by the CSS Working Group as a Working Draft using the Recommendation track. Publication as a Working Draft does not imply endorsement by W3C and its Members.

This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than a work in progress.

Please send feedback by filing issues in GitHub (preferred), including the spec code “css-anchor-position” in the title, like this: “[css-anchor-position] …summary of comment…”. All issues and comments are archived. Alternately, feedback can be sent to the (archived) public mailing list www-style@w3.org.

This document is governed by the 18 August 2025 W3C Process Document.

This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent that the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

1. Introduction

CSS absolute positioning allows authors to place boxes anywhere on the page, without regard to the layout of other boxes besides their containing block. This flexibility can be very useful, but also very limiting—​often you want to position relative to some other box. Anchor positioning (via the position-anchor and position-area properties and/or the anchor functions anchor() and anchor-size()) allows authors to achieve this, “anchoring” an absolutely positioned box to one or more other boxes on the page (its anchor references, while also allowing them to try several possible positions to find the “best” one that avoids overlap/overflow.

For example, an author might want to position a tooltip centered and above the targeted element, unless that would place the tooltip offscreen, in which case it should be below the targeted element. This can be done with the following CSS:
.anchor {
  anchor-name: --tooltip;
}
.tooltip {
  /* Fixpos means we don’t need to worry about
     containing block relationships;
     the tooltip can live anywhere in the DOM. */
  position: fixed;

  /* All the anchoring behavior will default to
     referring to the --tooltip anchor. */
  position-anchor: --tooltip;

  /* Align the tooltip’s bottom to the top of the anchor;
     this also defaults to horizontally center-aligning
     the tooltip and the anchor (in horizontal writing modes). */
  position-area: block-start;

  /* Automatically swap if this overflows the window
     so the tooltip’s top aligns to the anchor’s bottom
     instead. */
  position-try: flip-block;

  /* Prevent getting too wide */
  max-inline-size: 20em;
}

Note that using the Popover API will automatically set position and create the anchoring relationship without setting anchor-name or position-anchor value (by defining an implicit anchor element), so those properties wouldn’t need to be explicitly set again. So with the correct markup, this example can be simplified to:

.tooltip {
  /* Using the popover + popovertarget attributes sets 'position: fixed'
     and creates the necessary position-anchor relationship already. */
  position-area: block-start;
  position-try: flip-block;
  max-inline-size: 20em;
}

1.1. Value Definitions

This specification follows the CSS property definition conventions from [CSS2] using the value definition syntax from [CSS-VALUES-3]. Value types not defined in this specification are defined in CSS Values & Units [CSS-VALUES-3]. Combination with other CSS modules may expand the definitions of these value types.

In addition to the property-specific values listed in their definitions, all properties defined in this specification also accept the CSS-wide keywords as their property value. For readability they have not been repeated explicitly.

Like most operations in CSS besides selector matching, features in this specification operate over the flattened element tree.

2. Determining the Anchor

2.1. Creating an Anchor: the anchor-name property

Name: anchor-name
Value: none | <dashed-ident>#
Initial: none
Applies to: all elements that generate a principal box
Inherited: no
Percentages: n/a
Computed value: as specified
Canonical order: per grammar
Animation type: discrete

The anchor-name property declares that an element is an anchor element, whose principal box is an anchor box, and gives it a list of anchor names to be targeted by. Values are defined as follows:

none

The property has no effect.

<dashed-ident>#

If the element generates a principal box, the element is an anchor element, with a list of anchor names as specified. Each anchor name is a loosely matched tree-scoped name.

Otherwise, the property has no effect.

Anchor names do not need to be unique. Not all elements are capable of being the target anchor element of a given box. Thus a name can be reused in multiple places if the usages are scoped appropriately.

Note: If multiple elements share an anchor name and are all visible to a given positioned box, the target anchor element will be the last one in DOM order. The anchor-scope property can be used to further limit what names are visible to a given referencing box.

Anchor names are not scoped by containment by default; even if an element has style or layout containment (or any similar sort of containment), the anchor names of its descendants are visible to elements elsewhere in the page.

Note: While an element is in the skipped contents of another element (due to content-visibility: hidden, for instance), it’s not an acceptable anchor element, effectively acting as if it had no names.

Note: Positioned elements in shadow trees can reference anchor names defined in “higher” trees. Currently, they cannot reference anchor names defined in “lower” shadow trees, though.

2.1.1. Implicit Anchor Elements

Some specifications can define that, in certain circumstances, a particular element is an implicit anchor element for another element.

TODO: Fill in an example new popover-related details (once that finally lands in the HTML spec).

Implicit anchor elements can be referenced with the auto keyword in position-anchor, or by omitting the anchor reference in anchor functions.

The implicit anchor element of a pseudo-element is its originating element, unless otherwise specified.

2.1.2. The Anchor Box

Several features of this specification refer to the position and size of an anchor box. Unless otherwise specified, this refers to the border box edge of the principal box of the anchor element. The anchor box’s position and size is determined after layout.

This position and size includes zoom and position-based adjustments (such as position: relative or position: sticky) as well as transforms (such as transform or offset-path). In these cases, the axis-aligned bounding rectangle of the anchor box in the coordinate space of the absolutely positioned element’s containing block is used instead. Transforms are often optimized onto a different thread, so transform-based updates to an anchor box’s position may be delayed by a few frames. Authors can avoid this delay by using absolute or relative positioning instead where practical.

If the anchor box is fragmented, and the containing block of the absolutely positioned box referring to that anchor box is outside the relevant fragmentation context, the axis-aligned bounding rectangle of its box fragments is used instead. (If the absolutely positioned box is inside the fragmentation context, it sees the anchor box as unfragmented—​and can be itself fragmented by the fragmentation context.)

For performance reasons, scrolling is handled specially, see § 3.3 Taking Scroll Into Account. Other post-layout effects, such as filters, do not affect the anchor box’s position.

2.2. Scoping Anchor Names: the anchor-scope property

Name: anchor-scope
Value: none | all | <dashed-ident>#
Initial: none
Applies to: all elements
Inherited: no
Percentages: n/a
Computed value: as specified
Canonical order: per grammar
Animation type: discrete

This property scopes the specified anchor names, and lookups for these anchor names, to this element’s subtree. See § 2 Determining the Anchor.

Values have the following meanings:

none
No changes in anchor name scope.
all
Specifies that all anchor names defined by this element or its descendants—​whose scope is not already limited by a descendant using anchor-scope—​to be in scope only for this element’s descendants; and limits descendants to only match anchor names to anchor elements within this subtree.

This value only affects anchor names in the same tree scope, as if it were a strictly matched tree-scoped name. (That is, anchor-scope: all acts identically to anchor-scope: --foo, --bar, ..., listing all relevant anchor names.)

<dashed-ident>
Specifies that a matching anchor name defined by this element or its descendants—​whose scope is not already limited by a descendant using anchor-scope—​to be in scope only for this element’s descendants; and limits descendants to only match these anchor names to anchor elements within this subtree.

The <dashed-ident> represents a strictly matched tree-scoped name, i.e. it can only match against anchor names in the same shadow tree.[CSS-SCOPING-1]

This property has no effect on implicit anchor elements.

When a design pattern is re-used, anchor-scope can prevent naming clashes across identical components. For example, if a list contains positioned elements within each list item, which want to position themselves relative to the list item they’re in,
li {
  anchor-name: --list-item;
  anchor-scope: --list-item;
}
li .positioned {
  position: absolute;
  position-anchor: --list-item;
  position-area: inline-start;
}

Without anchor-scope, all of the li elements would be visible to all of the positioned elements, and so they’d all positioned themselves relative to the final li, stacking up on top of each other.

2.3. Finding an Anchor

Several things in this specification find a target anchor element, given an anchor specifier, which is either a <dashed-ident> (and a tree-scoped reference) that should match an anchor-name value elsewhere on the page, or the keyword auto, or nothing (a missing specifier).

Note: The general rule captured by these conditions is that an element can only be a positioned box’s target anchor element if its own box is fully laid out before the positioned box that wants to reference it is laid out. CSS’s layout rules provide some useful guarantees about this, depending on the anchor and positioned box’s relationship with each other and their containing blocks. The list of conditions below exactly rephrases the stacking context rules into just what’s relevant for this purpose, ensuring there is no possibility of circularity in anchor positioning.

To determine the target anchor element given a querying element query el and an optional anchor specifier anchor spec:
  1. If anchor spec was not passed, return the default anchor element if it exists, otherwise return nothing.

  2. If anchor spec is auto:

    1. If query el has an implicit anchor element that is an acceptable anchor element, return that element.

    2. Otherwise, return nothing.

    Note: Future APIs might also define implicit anchor elements. When they do, they’ll be explicitly handled in this algorithm, to ensure coordination.

  3. Otherwise, anchor spec is a <dashed-ident>. Return the last element el in tree order that satisfies the following conditions:

    If no element satisfies these conditions, return nothing.

    Note: anchor-scope can restrict the visibility of certain anchor names, which can affect what elements can be anchor elements for a given lookup.

Note: An anchor-name defined by styles in one shadow tree won’t be seen by anchor functions in styles in a different shadow tree, preserving encapsulation. However, elements in different shadow trees can still anchor to each other, so long as both the anchor-name and anchor function come from styles in the same tree, such as by using ::part() to style an element inside a shadow. (Implicit anchor elements also aren’t intrinsically limited to a single tree, but the details of that will depend on the API assigning them.)

An element possible anchor is an acceptable anchor element for an absolutely positioned element positioned el if all of the following are true:

2.4. Default Anchors: the position-anchor property

Name: position-anchor
Value: auto | <anchor-name>
Initial: auto
Applies to: absolutely positioned boxes
Inherited: no
Percentages: n/a
Computed value: as specified
Canonical order: per grammar
Animation type: discrete

The position-anchor property specifies the default anchor element, which is used by position-area, position-try, and (by default) all anchor functions applied to this element. position-anchor is a reset-only sub-property of position.

auto

Use the implicit anchor element if it exists; otherwise the box has no default anchor element.

<anchor-name>

The target anchor element selected by the specified <anchor-name> is the box’s default anchor element.

The principal box of the default anchor element is the box’s default anchor box.

For example, in the following code both .foo and .bar elements can use the same positioning properties, just changing the anchor element they’re referring to:
.anchored {
  position: absolute;
  top: calc(.5em + anchor(outside));
  /* Since no anchor name was specified,
     this automatically refers to the
     default anchor box. */
}

.foo.anchored {
  position-anchor: --foo;
}
.bar.anchored {
  position-anchor: --bar;
}

2.5. Anchor Relevance

When determining whether an element el is relevant to the user, if a descendant of el is a target anchor element for a positioned box (which itself is not skipped and whose containing block is not el or a descendant of el), then el must be considered relevant to the user.

Note: This means that, for example, an anchor in a content-visibility: auto subtree will prevent its subtree from skipping its contents as long as the positioned box relying on it is also not skipped. (Unless the anchor and the positioned box are both under the same content-visibility: auto element; they can’t cyclicly keep each other visible.)

3. Anchor-Based Positioning

An absolutely positioned box can position itself relative to one or more anchor boxes on the page.

The position-area property offers a convenient grid-based concept for positioning relative to the default anchor box; for more complex positioning or positioning relative to multiple boxes, the anchor() function can be used in the inset properties to explicitly refer to edges of an anchor box.

3.1. The position-area Property

Name: position-area
Value: none | <position-area>
Initial: none
Applies to: positioned boxes with a default anchor box
Inherited: no
Percentages: n/a
Computed value: the keyword none or a pair of keywords, see § 3.1.3 Computed Value and Serialization of <position-area>
Canonical order: per grammar
Animation type: TBD

Most common use-cases of anchor positioning are only concerned with the edges of the positioned box’s containing block and the edges of the default anchor box. These lines can be thought of as defining a 3×3 grid; position-area lets you easily specify what area of this position-area grid to lay out the positioned box in.

An example of position-area: top left positioning in a horizontal-tb ltr writing mode.
none

The property has no effect.

<position-area>

If the box does not have a default anchor box, or is not an absolutely positioned box, this value has no effect.

Otherwise, selects a region of the position-area grid, and makes that the box’s containing block.

Note: This means that the inset properties specify offsets from the position-area, and some property values, like max-height: 100%, will be relative to the position-area as well.

Values other than none have the following additional effects:

3.1.1. Resolving the Position Area Grid

The position-area grid is a 3×3 grid, composed of four grid lines in each axis. In order (using the writing mode of the containing block):

Note: When the default anchor box is partially or completely outside of the pre-modified containing block, some of the position-area grid’s rows or columns can be zero-sized.

3.1.2. Syntax of <position-area> Values

Positions are specified as a pair of values, which can be expressed in flow-relative or physical terms. The allowed syntax of a <position-area> value is:

<position-area> = [
  [ left | center | right | span-left | span-right
  | x-start | x-end | span-x-start | span-x-end
  | self-x-start | self-x-end | span-self-x-start | span-self-x-end
  | span-all ]
  ||
  [ top | center | bottom | span-top | span-bottom
  | y-start | y-end | span-y-start | span-y-end
  | self-y-start | self-y-end | span-self-y-start | span-self-y-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