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.
.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:20 em ; }
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 : 20 em ; }
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.
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.
-
If anchor spec was not passed, return the default anchor element if it exists, otherwise return nothing.
-
If anchor spec is auto:
-
If query el has an implicit anchor element that is an acceptable anchor element, return that element.
-
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.
-
-
Otherwise, anchor spec is a <dashed-ident>. Return the last element el in tree order that satisfies the following conditions:
-
el is an anchor element with an anchor name of anchor spec.
-
el’s anchor name and anchor spec are both associated with the same tree root.
Note: The anchor name is a tree-scoped name, while anchor spec is a tree-scoped reference.
-
el is an acceptable anchor element for query el.
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.)
-
possible anchor is either an element or a fully styleable tree-abiding pseudo-element.
-
possible anchor is in scope for positioned el, per the effects of anchor-scope on possible anchor or its ancestors.
-
possible anchor is laid out strictly before positioned el, aka one of the following is true:
-
possible anchor and positioned el have the same original containing block and either
-
possible anchor is in a lower top layer than positioned el, or
-
they both exist in the same top layer, but possible anchor is either not absolutely positioned or occurs earlier in the flat tree order than positioned el
-
-
The element generating possible anchor’s containing block (if one exists) is an acceptable anchor element for positioned el
-
-
If possible anchor is in the skipped contents of another element, then positioned el is in the skipped contents of that same element.
Note: In other words, positioned el can anchor to possible anchor if they’re both in the same skipped "leaf", but it can’t anchor "across" leafs. This means skipping an element that contains both of them won’t suddenly cause the positioned el to move to another anchor, but still prevents positioned elements elsewhere in the page from anchoring to the skipped element.
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.
.anchored{ position : absolute; top : calc ( .5 em +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.
- 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:
-
The scrollable containing block is used in place of the local containing block where applicable, so that the entire scrollable overflow area (typically) is available for positioning.
-
Any auto inset properties resolve to 0.
-
The normal value for the self-alignment properties resolves to a corresponding value, see § 4.1 Area-specific Default Alignment.
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):
-
the start edge of the box’s pre-modification containing block, or the start edge of the default anchor box if that is more start-ward
-
the start edge of the default anchor box
-
the end edge of the default anchor box
-
the end edge of the box’s pre-modification containing block, or the end edge of the default anchor box if that is more end-ward.
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