Title: CSS Anchor Positioning
Shortname: css-anchor-position
Level: 1
Status: ED
Group: csswg
Work Status: exploring
ED: https://drafts.csswg.org/css-anchor-position-1/
TR: https://www.w3.org/TR/css-anchor-position-1/
Editor: Tab Atkins-Bittner, Google, http://xanthir.com/contact/, w3cid 42199
Editor: Elika J. Etemad / fantasai, Apple, http://fantasai.inkedblade.net/contact, w3cid 35400
Former Editor: Jhey Tompkins, Google, https://twitter.com/jh3yy, w3cid 137616
Editor: Ian Kilpatrick, Google, w3cid 73001
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.
Ignored Terms: cssText, transitions
spec:aria-1.3; type: element-attr; text: role; for: html-global; url: https://w3c.github.io/aria/#host_general_role
Introduction {#intro} ===================== 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;
	}
	

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=]. Determining the Anchor {#determining} ====================== Creating an Anchor: the 'anchor-name' property {#name} ----------------------------------------------
Name: anchor-name
Value: none | <>#
Initial: none
Inherited: no
Applies to: all elements that generate a [=principal box=]
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. : <># :: 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 [=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 containment|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. ### Implicit Anchor Elements ### {#implicit} 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 ''position-anchor/auto'' keyword in 'position-anchor', or by omitting the anchor reference in [=anchor functions=]. [=Pseudo-elements=] have the same [=implicit anchor element=] as their [=originating element=], unless otherwise specified. Note: Without this, these [=pseudo-elements=], which are often inaccessible by other specifications, cannot be positioned with [=implicit anchor elements=]. ### The Anchor's Position ### {#determining-position} Several features of this specification refer to the position and size of an [=anchor box=]. The [=anchor box=]'s position and size is determined after layout, and for these purposes includes 'position'-based adjustments (such as ''position: relative'' or ''position: sticky''). Post-layout effects, such as 'transform', do not affect the [=anchor box's=] position. Note: Allowing an anchor to opt into including the effects of 'transform' or similar properties might be allowed in the future.

Scoping Anchor Names: the 'anchor-scope' property

Name: anchor-scope
Value: none | all | <>#
Initial: none
Applies to: all elements
Inherited: no
Animation type: discrete
Computed value: as specified
This property scopes the specified [=anchor names=], and lookups for these [=anchor names=], to this element's subtree. See [[#determining]]. 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.
<>
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.
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.
Finding an Anchor {#target} ----------------- Several things in this specification find a [=target anchor element=], given an anchor specifier, which is either a <> (and a [=tree-scoped reference=]) that should match an 'anchor-name' value elsewhere on the page, or the keyword ''position-anchor/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 possibly 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 ''position-anchor/auto'': 1. If the Popover API defines an [=implicit anchor element=] for |query el| which is an [=acceptable anchor element=] for |query el|, 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 <>. 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=] [=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.)
An element |possible anchor| is an acceptable anchor element for an [=absolutely positioned=] element |positioned el| if all of the following are true: * |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 |positioned el| or its ancestors. * |possible anchor| is laid out strictly before |positioned el|, aka one of the following is true: * |positioned el| is [=in a higher top layer=] than |possible anchor| * Both elements are [=in the same top layer=] but have different [=containing blocks=], and |positioned el|'s [=containing block=] is an ancestor of |possible anchor|'s [=containing block=] in the [=containing block chain=], aka one of the following: * |positioned el|'s [=containing block=] is the viewport, and |possible anchor|'s [=containing block=] isn't. * |positioned el|'s [=containing block=] is the [=initial containing block=], and |possible anchor|'s [=containing block=] is generated by an element, * both elements' [=containing blocks=] are generated by elements, and |positioned el|'s containing block is an ancestor in the [=flat tree=] to that of |possible anchor|'s [=containing block=]. * Both elements are [=in the same top layer=] and have the same [=containing block=], and are both [=absolutely positioned=], and |possible anchor| is earlier in [=flat tree=] order than |positioned el|. * Both elements are [=in the same top layer=] and have the same [=containing block=], but |possible anchor| isn't [=absolutely positioned=]. * 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.

Default Anchors: the 'position-anchor' property

Name: position-anchor
Value: auto | <>
Initial: auto
Applies to: [=absolutely positioned boxes=]
Inherited: no
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=]. : <> :: The [=target anchor element=] selected by the specified <> 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;
	}
	

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 contents|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 contents|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.) Anchor-Based Positioning {#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=]. The 'position-area' Property {#position-area} ---------------------------------------
Name: position-area
Value: none | <>
Initial: none
Inherited: no
Applies to: positioned boxes with a [=default anchor box=]
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. Its syntax is:
<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}
]
: none :: The property has no effect. : <> :: If the box does not have a [=default anchor box=], or is not an [=absolutely positioned box=], this value has no effect. Otherwise, it has the following effects: * The property selects a region of the [=position-area grid=], and makes that the box's [=containing block=]. See [[#resolving-spans]] for details. 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. * The ''align-self/normal'' value for the [=self-alignment properties=] behaves as either ''align-self/start'', ''align-self/end'', or ''align-self/anchor-center'', depending on the positioning of the region, to give a good default alignment for the positioned box. Again, see [[#resolving-spans]] for details. * Any ''top/auto'' [=inset properties=] resolve to ''0''. * The box is not considered to be in a [=legacy alignment mode=] in either axis.
If the box overflows its [=inset-modified containing block=], but would still fit within its [=original containing block=], by default it will “shift” to stay within its [=original containing block=], even if that violates its normal alignment. See [[css-align-3#overflow-values]] for details. This behavior makes it more likely that positioned boxes remain visible and within their intended bounds, even when their [=containing block=] ends up smaller than anticipated. For example, a ''position-area: bottom span-right'' value lets the positioned box stretch from its anchor's left edge to its containing block's right edge, and left-aligns it in that space by default. But if the positioned box is larger than that space (such as if the anchor is very close to the right edge of the screen), it will shift leftwards to stay visible.

Resolving <>s

The position-area grid is a 3×3 grid, composed of four grid lines in each axis. In order (and 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. A <> selects a region of this grid by specifying the rows and columns the region occupies, with each of the two keywords specifying one of them:
: start, end, self-start, self-end : top, bottom, left, right : y-start, y-end, y-self-start, y-self-end : x-start, x-end, x-self-start, x-self-end : block-start, block-end, block-self-start, block-self-end : inline-start, inline-end, inline-self-start, inline-self-end : center :: The single corresponding row or column, depending on which axis this keyword is specifying. Like in ''anchor()'', the plain logical keywords (''position-area/start'', ''position-area/end'', etc) refer to the [=writing mode=] of the box's [=containing block=]. The ''position-area/x-start''/etc determine their direction in the same way, but in the specified physical axis. The ''self-*'' logical keywords (''position-area/self-start'', ''position-area/x-self-end'', etc) are identical, but refer to the box's own [=writing mode=]. : span-start, span-end : span-top, span-bottom : span-y-start, span-y-end : span-x-start, span-x-end : span-block-start, span-block-end : span-inline-start, span-inline-end :: Two rows or columns, depending on which axis this keyword is specifying: the center row/column, and the row/column corresponding to the other half of the keyword as per the single-track keywords. (For example, ''span-top'' spans the first two rows-- the center row and the top row.) : span-all :: All three rows or columns, depending on which axis this keyword is specifying.
Some keywords are ambiguous about what axis they refer to: ''position-area/center'', ''position-area/span-all'', and the ''position-area/start''/etc keywords that don't specify the block or inline axis explicitly. If the other keyword is unambiguous about its axis, then the ambiguous keyword is referring to the opposite axis. (For example, in ''block-start center'', the ''position-area/center'' keyword is referring to the inline axis.) If both keywords are ambiguous, however, then the first refers to the [=block axis=] of the box's [=containing block=], and the second to the [=inline axis=]. (For example, ''span-all start'' is equivalent to ''span-all inline-start''.) If only a single keyword is given, it behaves as if the second keyword is ''position-area/span-all'' if the given keyword is unambigous about its axis; otherwise, it behaves as if the given keyword was repeated. (For example, ''position-area/top'' is equivalent to ''top span-all'', but ''position-area/center'' is equivalent to ''center center''.)
The <> also implies a default [=self-alignment=], which will be used if the [=self-alignment property=] on the box is ''align-self/normal'': * If the only the center region in an axis is selected, or all three regions are selected, the default alignment in that axis is ''align-self/anchor-center''. * Otherwise, the default alignment in that axis is toward the non-specified side region: if it's specifying the "start" region of its axis, the default alignment in that axis is ''align-self/end''; etc.
For example, assuming an English-equivalent writing mode (horizontal-tb, ltr), then the value ''span-x-start top'' resolves to the ''position-area/start'' region of the vertical axis, and the ''position-area/start'' and ''position-area/center'' regions of the horizontal axis, so the default alignments will be ''align-self: end'' (making the box's bottom [=margin edge=] flush with the bottom of the ''position-area/top'' region) and ''justify-self: end'' (making the box's end-side [=margin edge=] flush with the end side of the ''position-area/span-start'' region).
An example of ''position-area: span-x-start top'' positioning.
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. The ''anchor()'' Function {#anchor-pos} --------------------------------------------------- An [=absolutely positioned box=] can use the anchor() function as a value in its [=inset properties=] to refer to the position of one or more [=anchor boxes=]. The ''anchor()'' function resolves to a <>. It is only valid to be used in the [=inset properties=].
Name: top, left, right, bottom
New values: <>
	<anchor()> = anchor( <>? && <>, <>? )
	<> = <>
	<> = inside | outside
			   | top | left | right | bottom
			   | start | end | self-start | self-end
			   | <> | center
The ''anchor()'' function has three arguments: * the <> value specifies how to find the [=anchor element=] it will be drawing positioning information from. Its possible values are:
: <> :: Specifies the [=anchor name=] it will look for. This name is a [=tree-scoped reference=]. : omitted :: Selects the [=default anchor element=] defined for the box, if possible.
See [=target anchor element=] for details. * the <> value refers to the position of the corresponding side of the [=target anchor element=]. Its possible values are:
: inside : outside :: Resolves to one of the [=anchor box's=] sides, depending on which [=inset property=] it's used in. ''anchor()/inside'' refers to the same side as the [=inset property=] (attaching the positioned box to the "inside" of the [=anchor box=]), while ''anchor()/outside'' refers to the opposite. : top : right : bottom : left :: Refers to the specified side of the [=anchor box=]. Note: These are only usable in the [=inset properties=] in the matching axis. For example, ''anchor()/left'' is usable in 'left', 'right', or the logical [=inset properties=] that refer to the horizontal axis. : start : end : self-start : self-end :: Refers to one of the sides of the [=anchor box=] in the same axis as the [=inset property=] it's used in, by resolving the keyword against the [=writing mode=] of either the positioned box (for ''anchor()/self-start'' and ''anchor()/self-end'') or the positioned box's containing block (for ''anchor()/start'' and ''anchor()/end''). : <> : center :: Refers to a position a corresponding percentage between the ''anchor()/start'' and ''anchor()/end'' sides, with ''0%'' being equivalent to ''anchor()/start'' and ''100%'' being equivalent to ''anchor()/end''. ''anchor()/center'' is equivalent to ''50%''. * the optional <> final argument is a fallback value, specifying what the function should resolve to if it's an [=invalid anchor function=]. An ''anchor()'' function representing a [=valid anchor function=] resolves at [=computed value=] time (using [=style & layout interleaving=]) to the <> that would align the edge of the positioned boxes' [=inset-modified containing block=] corresponding to the property the function appears in with the specified border edge of the [=target anchor element=], assuming that all [=scroll containers=] between the [=target anchor element=] and the positioned box's [=containing block=] are scrolled to their initial scroll position (but see [[#scroll]]). Note: This means that [=transitions=] or [=animations=] of a property using an [=anchor function=] will work "as expected" for all sorts of possible changes: the [=anchor box=] moving, [=anchor elements=] being added or removed from the document, the 'anchor-name' property being changed on anchors, etc. If the [=target anchor element=] is [=fragmented=], the axis-aligned bounding rectangle of the [=box fragment|fragments=]' [=border boxes=] is used instead.
For example, in ''.bar { inset-block-start: anchor(--foo block-start); }'', the ''anchor()'' will resolve to the length that'll line up the .bar element's block-start edge with the ''--foo'' anchor's block-start edge. On the other hand, in ''.bar { inset-block-end: anchor(--foo block-start); }'', it will instead resolve to the length that'll line up the .bar element's block-end edge with the ''--foo'' anchor's block-start edge. Since 'inset-block-start' and 'inset-block-end' values specify insets from different edges (the block-start and block-end of the element's [=containing block=], respectively), the same ''anchor()'' will usually resolve to different lengths in each.
Because the ''anchor()'' function resolves to a <>, it can be used in [=math functions=] like any other length. For example, the following will set up the element so that its [=inset-modified containing block=] is centered on the [=anchor box=] and as wide as possible without overflowing the [=containing block=]:
	.centered-message {
		position: fixed;
		max-width: max-content;
		justify-self: center;

		--center: anchor(--x 50%);
		--half-distance: min(
			abs(0% - var(--center)),
			abs(100% - var(--center))
		);
		left: calc(var(--center) - var(--half-distance));
		right: calc(var(--center) - var(--half-distance));
		bottom: anchor(--x top);
	}
	
This might be appropriate for an error message on an <{input}> element, for example, as the centering will make it easier to discover which input is being referred to.
Centering on the Anchor: the ''anchor-center'' value {#anchor-center} ------------------------------------------------------------------------
Name: justify-self, align-self, justify-items, align-items
New Values: anchor-center
The [=self-alignment properties=] allow an [=absolutely positioned box=] to align itself within the [=inset-modified containing block=]. The existing values, plus carefully chosen [=inset properties=], are usually enough for useful alignment, but a common case for anchored positioning-- centering over the [=anchor box=]-- requires careful and somewhat complex set-up to achieve. The new anchor-center value makes this case extremely simple: if the positioned box has a [=default anchor box=], then it is centered (insofar as possible) over the [=default anchor box=] in the relevant axis. Additionally, any ''top/auto'' [=inset properties=] resolve to ''0''. If the box is not [=absolutely positioned=], or does not have a [=default anchor box=], this value behaves as ''/center'' and has no additional effect on how [=inset properties=] resolve. Note: Similar to 'position-area', when using ''anchor-center'', if the anchor is too close to the edge of the box's [=original containing block=], by default it will “shift” from being purely centered, in order to remain within the [=original containing block=]. See [[css-align-3#overflow-values]] for more details. Taking Scroll Into Account {#scroll} -------------------------- For performance reasons, implementations usually perform scrolling on a separate scrolling/"compositing" thread, which has very limited capabilities (simple movement/transforms/etc., but no layout or similar expensive operations) and thus can be relied upon to respond to scrolling fast enough to be considered "instant" to human perception. If scrolling just causes an anchor-positioned element to move, there is in theory no issue; the movement can be performed on the scrolling thread so the positioned element moves smoothly with the scrolling content. However, [=anchor positioning=] allows an element to make the positions of its own opposite edges depend on things in different scrolling contexts, which means scrolling could move just one edge and cause a size change, and thus perform layout. This can't be performed on the scrolling thread! To compensate for this, while still allowing as much freedom to anchor to various elements as possible, [=anchor positioning=] uses a combination of [=remembered scroll offsets=] and [=compensating for scroll=].
The details here are technical, but the gist is: * When a positioned element is first displayed, or when it [[#fallback|changes fallbacks]], its position is correctly calculated according to the up-to-date position of all [=anchor references=]. If these [=anchor references=] are in a different scroll context, their total scroll offsets are memorized, and layout will continue using those memorized offsets, even if those elements are scrolled later. (Only the scroll offsets are memorized; their actual laid-out positions are freshly calculated each time and remain accurate.) They'll only recalculate if the positioned element stops being displayed and starts again, or changes fallbacks. * The one exception to this is the [=default anchor element=]; if it's scrolled away from its [=remembered scroll offset=], the positioned element moves with it. Because this is *purely* a shift in position, the positioned element can't change size or otherwise require layout in response. The end result is that [=anchor positioning=] should generally "just work", regardless of what the element is anchoring to, but it might be limited in how it can respond to scrolling.
An anchor recalculation point occurs for an [=absolutely positioned element=] whenever that element begins generating boxes (aka switches from ''display:none'' or ''display:contents'' to any other 'display' value), identical to when it starts running CSS animations. An [=anchor recalculation point=] also occurs for an element when [=determining position fallback styles=] for that element; if it changes fallback styles as a result, it uses the result of the [=anchor recalculation point=] associated with the chosen set of fallback styles. When an [=anchor recalculation point=] occurs for an element |abspos|, then for every element |anchor| referenced by one of |abspos|'s [=anchor references=], it associates a remembered scroll offset equal to the current sum of the [=scroll offsets=] of all [=scroll container=] ancestors of |anchor|, up to but not including |abspos|'s [=containing block=]. The [=remembered scroll offset=] also accounts for other scroll-dependent positioning changes, such as ''position: sticky''. If |abspos| has a [=default anchor element=], it always calculates a [=remembered scroll offset=] for it, even if |abspos| doesn't actually have an [=anchor reference=] to it. All [=anchor references=] are calculated as if all [=scroll containers=] were at their [=initial scroll position=], and then have their associated [=remembered scroll offset=] added to them. Issue: Transforms have the same issue as scrolling, so Anchor Positioning similarly doesn't pay attention to them normally. Can we go ahead and incorporate the effects of transforms here?
The above allows a positioned element to respond to the scroll positions of its [=anchor references=] once, but if any of them are scrolled, the positioned element will no longer appear to be anchored to them (tho it will continue to respond to their non-scrolling movement). While this problem can't be solved in general, we can respond to the scrolling of one [=anchor reference=]; specifically, the [=default anchor element=]:
An [=absolutely positioned box=] |abspos| compensates for scroll in the horizontal or vertical axis if both of the following conditions are true: * |abspos| has a [=default anchor box=]. * |abspos| has an [=anchor reference=] to its [=default anchor box=] or at least to something in the same scrolling context, aka at least one of: * |abspos|'s used [=self-alignment property=] value in that axis is ''anchor-center''; * |abspos| has a non-''position-area/none'' value for 'position-area' * at least one ''anchor()'' function on |abspos|'s [=used value|used=] [=inset properties=] in the axis refers to a [=target anchor element=] with the same nearest [=scroll container=] ancestor as |abspos|'s [=default anchor box=]. Note: If |abspos| has a [=position options list=], then whether it [=compensates for scroll=] in an axis is also affected by the applied fallback style. |abspos|'s default scroll shift is a pair of lengths for the horizontal and vertical axises, respectively. Each length is calculated as: * If |abspos| is [=compensating for scroll=] in the axis, then the length is the difference between the [=remembered scroll offset=] of the [=default anchor element=] and what its current [=remembered scroll offset=] would be if it were recalculated. * Otherwise, the length is 0. After layout has been performed for |abspos|, it is additionally shifted by the [=default scroll shift=], as if affected by a [=transform=] (before any other transforms). Issue: Define the precise timing of the snapshot: updated each frame, before style recalc. Issue: Similar to [=remembered scroll offset=], can we pay attention to transforms on the [=default anchor element=]? Note: While [=remembered scroll offsets=] affect the value of ''anchor()'' functions, [=default scroll shift=] directly shifts the element, after determining the value of its [=inset properties=], applying alignment, etc. This is usually indistinguishable, but cases like ''round(anchor(outside), 50px)'', which transform the [=default anchor element's=] position in a non-linear fashion, will expose the difference in behavior.
Validity {#anchor-valid} -------- An ''anchor()'' function is a valid anchor function only if all the following conditions are true: * It's being used in an [=inset property=] on an [=absolutely positioned box=]. * If its <> specifies a physical keyword, it's being used in an [=inset property=] in that axis. (For example, ''anchor()/left'' can only be used in 'left', 'right', or a logical [=inset property=] in the horizontal axis.) * The result of determining the [=target anchor element=] is not nothing when given the querying element as the element it's used on, and the anchor specifier as the <> value specified in the function. If any of these conditions are false, the ''anchor()'' function resolves to its specified fallback value. If no fallback value is specified, it makes the declaration referencing it [=invalid at computed-value time=]. Conditional Hiding: the 'position-visibility' property {#position-visibility} ------------------------------------------------------
Name: position-visibility
Value: always | [ anchors-valid || anchors-visible || no-overflow ]
Initial: anchors-visible
Applies to: [=absolutely positioned boxes=]
Percentages: n/a
Inherited: no
Animation type: discrete
There are some conditions in which it might not make sense to display an [=absolutely positioned box=]. This property allows such boxes to be made conditionally visible, depending on some commonly needed layout conditions.
: always :: This property has no effect. (The box is displayed without regard for its anchors or its overflowing status.) : anchors-valid :: If any of the box's [=required anchor references=] do not resolve to a [=target anchor element=], the box is [=strongly hidden=]. Issue: What is a required anchor reference? ''anchor()'' functions that don't have a fallback value; the default anchor *sometimes*? Need more detail here. Issue: Any anchors are missing, or all anchors are missing? I can see use-cases for either, potentially. Do we want to make a decision here, or make it controllable somehow? : anchors-visible :: If the box has a [=default anchor box=] but that [=anchor box=] is [=clipped by intervening boxes=], this box is also [=strongly hidden=]. : no-overflow :: If the box overflows its [=inset-modified containing block=] even after applying 'position-try', then the box is [=strongly hidden=].
An anchor box |anchor| is clipped by intervening boxes relative to a positioned box |abspos| relying on it if |anchor|'s [=ink overflow rectangle=] is fully clipped by a box which is an ancestor of |anchor| but a descendant of |abspos|'s containing block. Clipping in this case refers only to clipping due to 'overflow', or other effects (such as [=paint containment=]) that clip to the [=overflow clip edge=]. Note: This means that if an abspos is next to its anchor in the DOM, for example, it'll remain visible even if its default anchor is scrolled off, since it's clipped by the same scroller anyway. Issue: Make sure this definition of clipped is consistent with View Transitions, which wants a similar concept. |anchor| is also considered [=clipped by intervening boxes=] if it is [=strongly hidden=] by 'position-visibility'. Note: This ensures that in a "chained anchor" situation, if the first abspos is hidden due to this property (due to its anchor being scrolled off), then another abspos using it as an anchor will also be hidden, rather than also floating in a nonsensical location.
A box that is strongly hidden acts as if it and all of its descendants were ''visibility: hidden'', regardless of what their actual 'visibility' value is. Accessibility Implications {#accessibility} -------------------------- It's important to remember that Anchor Positioning does not automatically establish any semantic relationship between a positioned box and any of its anchors, because it can be used in many different ways. Authors must not rely solely on a visual connection implied by the positioning to link elements together semantically; without additional help, the elements often have no meaningful DOM relationship, making them difficult or impossible to use in non-visual user agents, like screen readers. Many features on the web platform, both existing and upcoming, allow establishing such connections explicitly, so that non-visual user agents can also benefit. For example, the Popover API in HTML automatically links the invoker button to the popover element, including automatically adjusting tabbing order; it also establishes the invoker button as the [=implicit anchor element=] for the popover, making it easy to use Anchor Positioning as well. In more general cases, ARIA features such as the aria-details or aria-describedby attributes on an anchor element can provide this information in a slightly more manual fashion; in concert with the <{html-global/role}> attribute on the positioned element, non-visual user agents can tell their users about the relationship between the elements and let them automatically navigate between them. Anchor-Based Sizing {#sizing} =================== An [=absolutely positioned box=] can use the anchor-size() function in its [=sizing properties=] to refer to the size of one or more [=anchor boxes=]. The ''anchor-size()'' function resolves to a <>. It is only valid to be used in the [=accepted @position-try properties=]. The ''anchor-size()'' Function {#anchor-size-fn} ------------------------------
Name: width, height, min-width, min-height, max-width, max-height, top, left, right, bottom, margin-top, margin-left, margin-right, margin-bottom 
New values:	<>
anchor-size() = anchor-size( [ <> || <> ]? , <>? )
<> = width | height | block | inline | self-block | self-inline
The ''anchor-size()'' function is similar to ''anchor()'', and takes the same arguments, save that the <> keywords are replaced with <>, referring to the distance between two opposing sides. The physical <> keywords (width and height) refer to the width and height, respectively, of the [=target anchor element=]. Unlike ''anchor()'', there is no restriction on having to match axises; for example, ''width: anchor-size(--foo height);'' is valid. The logical <> keywords (block, inline, self-block, and self-inline) map to one of the physical keywords according to either the [=writing mode=] of the box (for ''self-block'' and ''self-inline'') or the [=writing mode=] of the box's [=containing block=] (for ''anchor-size()/block'' and ''anchor-size()/inline''). If the <> keyword is omitted, it defaults to behaving as whatever keyword matches the axis of the property that ''anchor-size()'' is used in. (For example, ''width: anchor-size()'' is equivalent to ''width: anchor-size(width)''.) An ''anchor-size()'' function representing a [=valid anchor-size function=] resolves at [=computed value=] time (via [=style & layout interleaving=]) to the <> separating the relevant border edges (either left and right, or top and bottom, whichever is in the specified axis) of the [=target anchor element=]. Validity {#anchor-size-valid} -------- An ''anchor-size()'' function is a valid anchor-size function only if all the following conditions are true: * It's being used in a [=sizing property=], an [=inset property=], or a [=margin property=] on an [=absolutely positioned box=]. (These are the [=accepted @position-try properties=] that allow lengths.) * There is a [=target anchor element=] for the box it's used on, and the <> value specified in the function. If any of these conditions are false, the ''anchor-size()'' function resolves to its specified fallback value. If no fallback value is specified, it makes the declaration referencing it [=invalid at computed-value time=]. Overflow Management {#fallback} =========================== Anchor positioning, while powerful, can also be unpredictable. The [=anchor box=] might be anywhere on the page, so positioning a box in any particular fashion (such as above the anchor, or the right of the anchor) might result in the positioned box overflowing its [=containing block=] or being positioned partially off screen. To ameliorate this, an [=absolutely positioned=] box can use the 'position-try-fallbacks' property to refer to several variant sets of positioning/alignment properties (generated from the box's existing styles, or specified in ''@position-try'' rules) that the UA can try if the box overflows its initial position. Each is applied to the box, one by one, and the first that doesn't cause the box to overflow its [=containing block=] is taken as the winner. 'position-try-order' allows these options to additional be sorted by the available space they define, if it's more important for the box to have as much space as possible rather than strictly follow some declared order. Giving Fallback Options: the 'position-try-fallbacks' property {#position-try-fallbacks} --------------------------------
Name: position-try-fallbacks
Value: none | [ [<> || <>] | <<'position-area'>> ]#
Initial: none
Inherited: no
Applies to: [=absolutely positioned boxes=]
Animation type: discrete
This property provides a list of alternate positioning styles to try when the [=absolutely positioned box=] overflows its [=inset-modified containing block=]. This position options list is initially empty. Each comma-separated entry in the list is a separate option: either the name of a ''@position-try'' block, or a <> representing an automatic transformation of the box's existing computed style. Values have the following meanings:
: none :: The property has no effect; the box's [=position options list=] is empty. : <> :: If there is a ''@position-try'' rule with the given name, its associated [=position option=] is added to the [=position options list=]. Otherwise, this value has no effect. : <> :: Automatically creates a [=position option=] from the box's computed style, by [=swapping due to a try-tactic=] according to the specified keyword, and adding the constructed [=position option=] to the box's [=position options list=]. and adds it to the [=position options list=].
		<> = flip-block || flip-inline || flip-start
		
: flip-block :: swaps the values in the [=block axis=] (between, for example, 'margin-block-start' and 'margin-block-end'), essentially mirroring across an [=inline-axis=] line. : flip-inline :: swaps the values in the [=inline axis=], essentially mirroring across a [=block-axis=] line. : flip-start :: swaps the values of the [=start=] properties with each other, and the [=end=] properties with each other (between, for example, 'margin-block-start' and 'margin-inline-start'), essentially mirroring across a diagonal drawn from the [=block-start|start=]-[=inline-start|start=] corner to the [=block-end|end=]-[=inline-end|end=] corner. If multiple keywords are given, the transformations are composed in order to produce a single [=position option=]. : <> || <> :: Combines the effects of the previous two options: if there is a ''@position-try'' rule with the given name, applies its [=position option=] to the base style, then transforms it according to the specified <> and adds the result to the box's [=position options list=]. Otherwise, does nothing. : <<'position-area'>> :: Automatically creates a [=position option=] composed solely of a 'position-area' property with the given value.
To swap due to a try-tactic the styles of a box's element |el| between two directions |directions|, returning a set of styles: 0. If |directions| are opposites along the same axis, they are “opposing”. Otherwise (when they are specifying different axises), they are “perpendicular”. 1. Determine the specified values of the [=accepted @position-try properties=] on |el|, and let |styles| be the result. 2. [=substitute a var()|Substitute variables=], ''env()'' functions, and similar [=arbitrary substitution functions=] in |styles|. For ''env()'' functions, if the referenced [=CSS/environment variable=] is associated with a direction or axis (such as ''env()/safe-area-inset-top''), switch the referenced [=CSS/environment variable=] corresponding to |directions|.
For example, if ''top: env(safe-area-inset-top);'' is specified, and |directions| are up and left, the ''env()'' will resolve as if ''env(safe-area-inset-left)'' had been specified instead. (And then, in the next step, will actually swap into the 'left' property.)
3. Swap the values of the |styles| between the associated properties corresponding to |directions|.
For example, if "top" and "left" are being swapped, then the values of 'margin-top' and 'margin-left' are swapped, 'width' and 'height' are swapped, etc.
Note: If the directions are opposites along the same axis, some properties (like 'width' or 'align-self') wont' swap, since they're associated with themselves across the two directions, but their values might be changed by the next step. 4. Modify the values of the properties as they swap to match the new directions, as follows: * For [=inset properties=], change the specified side in ''anchor()'' functions to maintain the same relative relationship to the new direction that they had to the old. If a <> is used, and |directions| are opposing, change it to ''100%'' minus the original percentage.
For example, if "top" and "left" are being swapped, then ''margin-top: anchor(bottom)'' will become ''margin-left: anchor(right)''. If "top" and "bottom" are being swapped, then ''margin-top: anchor(20%)'' will become ''margin-bottom: anchor(80%)''.
* For [=sizing properties=], change the specified axis in ''anchor-size()'' functions to maintain the same relative relationship to the new direction that they had to the old.
For example, if "top" and "left" are being swapped, then ''width: anchor-size(width)'' will become ''height: anchor-size(height)''.
* For the [=self-alignment properties=], if |directions| are opposing, change the specified <> (or ''align-self/left''/''align-self/right'' keywords), if any, to maintain the same relative relationship to the new direction that they had to the old.
For example, if "top" and "bottom" are being swapped, then ''align-self: start'' will become ''align-self: end''. However, ''align-self: center'' will remain unchanged, as it has the same relationship to both directions. Similarly, ''align-self: first baseline'' will remain unchanged, as it's a <> rather than a <>.
* For 'position-area', change the value so that the selected rows/columns of the [=position-area grid=] maintain the same relative relationship to the new direction that they had to the old.
For example, if "top" and "left" are being swapped, then ''position-area: left span-bottom'' will become ''position-area: top span-right''.
5. Return |styles|.
Determining Fallback Order: the 'position-try-order' property {#position-try-order-property} ----------------------------------
Name: position-try-order
Value: normal | <>
Initial: normal
Applies to: [=absolutely positioned boxes=]
Inherited: no
Animation Type: discrete
This property specifies the order in which the [=position options list=] will be tried.
<> = most-width | most-height | most-block-size | most-inline-size
: normal :: Try the [=position options=] in the order specified by 'position-try-fallbacks'. : most-width : most-height : most-block-size : most-inline-size :: For each entry in the [=position options list=], [=apply a position option|apply that position option=] to the box, and find the specified [=inset-modified containing block=] size that results from those styles. Stably sort the [=position options list=] according to this size, with the largest coming first.
For example, the following styles will initially position the popup list below its anchoring button, but if that overflows, will decide whether to keep the popup list below the anchor or move it above, depending on which option gives it the most space.
	.anchor { anchor-name: --foo; }
	.list {
		position: fixed;
		position-anchor: --foo;
		position-area: block-end span-inline-end;
		align-self: start;
		position-try-fallbacks: --bottom-scrollable, flip-block, --top-scrollable;
		position-try-order: most-height;
	}
	@position-try --bottom-scrollable {
		align-self: stretch;
	}
	@position-try --top-scrollable {
		position-area: block-start span-inline-end;
		align-self: stretch;
	}
	
The ''flip-block'' auto-generated option and the ''--top-scrollable'' option will always find the same available height, since both of them stretch vertically from the top edge of the containing block to the top of the anchor, so they'll retain their specified order. This causes the box to first try to align against the anchor at its natural height (using ''align-self: end'', auto-reversed from the base styles), but if that also causes overflow, it'll fall back to just filling the space and being scrollable instead.
The 'position-try' Shorthand {#position-try-prop} -------------------------------------------------
Name: position-try
Value: <<'position-try-order'>>? <<'position-try-fallbacks'>>
This shorthand sets both 'position-try-fallbacks' and 'position-try-order'. If <<'position-try-order'>> is omitted, it's set to the property's initial value. The ''@position-try'' Rule {#fallback-rule} ------------------------------- The @position-try rule defines a position option with a given name, specifying one or more sets of positioning properties that can be applied to a box via 'position-try-fallbacks', The syntax of the ''@position-try'' rule is:
@position-try <> {
	<>
}
The <> specified in the prelude is the rule's name. If multiple ''@position-try'' rules are declared with the same name, the last one in document order "wins". The ''@position-try'' rule only accepts the following [=properties=]: * [=inset properties=] * [=margin properties=] * [=sizing properties=] * [=self-alignment properties=] * 'position-anchor' * 'position-area' It is invalid to use ''!important'' on the properties in the <>. Doing so causes the property it is used on to become invalid, but does not invalidate the ''@property-try'' rule as a whole. All of the properties in a ''@position-try'' are applied to the box as part of the Position Fallback Origin, a new [=cascade origin=] that lies between the [=Author Origin=] and the [=Animation Origin=]. Similar to the [=Animation Origin=], use of the ''revert'' value acts as if the property was part of the [=Author Origin=], so that it instead reverts back to the [=User Origin=]. (As with the [=Animation Origin=], however, ''revert-layer'' has no special behavior and acts as specified.) Note: The [=accepted @position-try properties=] are the smallest group of properties that affect just the size and position of the box itself, without otherwise changing its contents or styling. This significantly simplifies the implementation of position fallback while addressing the fundamental need to move an anchor-positioned box in response to available space. Since these rules override normal declarations in the [=Author Origin=], this also limits the poor interactions of ''@position-try'' declarations with the normal cascading and inheritance of other properties. It is expected that a future extension to [=container queries=] will allow querying an element based on the position fallback it's using, enabling the sort of conditional styling not allowed by this restricted list. Note: If multiple boxes using different anchors want to use the same fallback positioning, just relative to their own anchor elements, omit the <> in ''anchor()'' and specify each box's anchor in 'position-anchor' instead. Note: The most common types of fallback positioning (putting the positioned box on one side of the anchor normally, but flipping to the opposite side if needed) can be done automatically with keywords in 'position-try-fallbacks', without using ''@position-try'' at all. Applying Position Fallback {#fallback-apply} -------------------------- When a positioned box (shifted by its [=default scroll shift=]) overflows its [=inset-modified containing block=], and has a non-empty [=position options list=], it [=determines position fallback styles=] to attempt to find an option that avoids overflow. These modified styles are applied to the element via [=interleaving=], so they affect [=computed values=] (and can trigger transitions/etc) even though they depend on layout and [=used values=].
To apply a position option to a box's element |el|, given a [=position option=] |new styles|: 1. With |new styles| inserted into the cascade in the [=position fallback origin=], resolve the cascade, and perform enough layout to determine |el|'s [=used value|used styles=]. For the purpose of calculating these styles, a hypothetical [=anchor recalculation point=] is calculated, and the resulting hypothetical [=remembered scroll offsets=] are used to determine |el|'s styles. 2. Return |el|'s [=used value|used styles=].
To determine position fallback styles for an element |abspos|: 1. Let |current styles| be the current used styles of |abspos| (which might be the result of earlier fallback). 2. [=list/For each=] |option| in the [=position options list=]: 1. If |option| is currently |abspos|'s [=last successful position option=], [=iteration/continue=]. 2. Let |adjusted styles| be the result of [=applying a position option=] |option| to |abspos|. 3. Let |el rect| be the size and position of |abspos|'s margin box, and |cb rect| be the size and position of |abspos|'s [=inset-modified containing block=], when laid out with |adjusted styles|. 4. If |cb rect| was negative-size in either axis and corrected into zero-size, [=iteration/continue=]. Note: This prevents a zero-size |el rect| from still being considered "inside" a negative-size |cb rect| and getting selected as a successful option. 5. If |el rect| is not fully contained within |cb rect|, [=iteration/continue=]. 6. Return |adjusted styles|, along with the associated set of [=remembered scroll offsets=] that were hypothetically calculated for them. 3. Assert: The previous step finished without finding a [=position option=] that avoids overflow. 4. Return |current styles|. Note: Descendants overflowing |el| don't affect this calculation, only |el|'s own [=margin box=]. Note: Because we purposely skip the [=position option=] currently in effect, it doesn't get its [=remembered scroll offsets=] updated; if none of the other fallbacks work and we stick with the current styles, all the [=remembered scroll offsets=] stay the same.
During a full layout pass, once a box has determined its fallback styles (or determined it's not using any), laying out later boxes cannot change this decision.
For example, say you have two positioned boxes, A and B, with A laid out before B. If B overflows and causes A's containing block to gain scrollbars, this does not cause A to go back and re-determine its fallback styles in an attempt to avoid overflowing. (At best, this can result in exponential layout costs; at worst, it's cyclic and will never settle.) Layout does not "go backward", in other words.
At the time that {{ResizeObserver}} events are determined and delivered: * If a box |el| is [=absolutely positioned=], set its last successful position option to the set of [=accepted @position-try properties=] (and values) that it's currently using. * Otherwise, if |el| has a [=last successful position option=] and if any of the following are true of it, remove its [=last successful position option=]: * |el| is not [=absolutely positioned=] * |el|'s computed value for 'position-try-fallbacks' has changed * Any of the ''@position-try'' rules referenced by |el|'s 'position-try-fallbacks' have been added, removed, or mutated. Then, [=determine position fallback styles=] for |el| and set its [=last successful position option=] to the set of [=accepted @position-try properties=] (and values) that it's now using. Note: The timing of this recording/removal is intentionally identical to the treatment of [=last remembered sizes=].
Implementations may choose to impose an implementation-defined limit on the length of [=position options lists=], to limit the amount of excess layout work that may be required. This limit must be at least five.
For example, the following CSS will first attempt to position a "popover" below the element, but if it doesn't fit on-screen will switch to being above; it defaults to left-aligning, but will switch to right-aligning if that doesn't fit.
	#myPopover {
		position: fixed;
		top: anchor(--button bottom);
		left: anchor(--button left);
		position-try-fallbacks: flip-inline, flip-block, flip-block flip-inline;

		/* The popover is at least as wide as the button */
		min-width: anchor-size(--button width);

		/* The popover is at least as tall as 2 menu items */
		min-height: 6em;
	}
	
DOM Interfaces {#interfaces} ========== The CSSPositionTryRule interface {#om-position-try} ------------------------------------- The {{CSSPositionTryRule}} interface represents the ''@position-try'' rule: [Exposed=Window] interface CSSPositionTryRule : CSSRule { readonly attribute CSSOMString name; [SameObject, PutForwards=cssText] readonly attribute CSSPositionTryDescriptors style; }; [Exposed=Window] interface CSSPositionTryDescriptors : CSSStyleDeclaration { attribute CSSOMString margin; attribute CSSOMString marginTop; attribute CSSOMString marginRight; attribute CSSOMString marginBottom; attribute CSSOMString marginLeft; attribute CSSOMString marginBlock; attribute CSSOMString marginBlockStart; attribute CSSOMString marginBlockEnd; attribute CSSOMString marginInline; attribute CSSOMString marginInlineStart; attribute CSSOMString marginInlineEnd; attribute CSSOMString margin-top; attribute CSSOMString margin-right; attribute CSSOMString margin-bottom; attribute CSSOMString margin-left; attribute CSSOMString margin-block; attribute CSSOMString margin-block-start; attribute CSSOMString margin-block-end; attribute CSSOMString margin-inline; attribute CSSOMString margin-inline-start; attribute CSSOMString margin-inline-end; attribute CSSOMString inset; attribute CSSOMString insetBlock; attribute CSSOMString insetBlockStart; attribute CSSOMString insetBlockEnd; attribute CSSOMString insetInline; attribute CSSOMString insetInlineStart; attribute CSSOMString insetInlineEnd; attribute CSSOMString top; attribute CSSOMString left; attribute CSSOMString right; attribute CSSOMString bottom; attribute CSSOMString inset-block; attribute CSSOMString inset-block-start; attribute CSSOMString inset-block-end; attribute CSSOMString inset-inline; attribute CSSOMString inset-inline-start; attribute CSSOMString inset-inline-end; attribute CSSOMString width; attribute CSSOMString minWidth; attribute CSSOMString maxWidth; attribute CSSOMString height; attribute CSSOMString minHeight; attribute CSSOMString maxHeight; attribute CSSOMString blockSize; attribute CSSOMString minBlockSize; attribute CSSOMString maxBlockSize; attribute CSSOMString inlineSize; attribute CSSOMString minInlineSize; attribute CSSOMString maxInlineSize; attribute CSSOMString min-width; attribute CSSOMString max-width; attribute CSSOMString min-height; attribute CSSOMString max-height; attribute CSSOMString block-size; attribute CSSOMString min-block-size; attribute CSSOMString max-block-size; attribute CSSOMString inline-size; attribute CSSOMString min-inline-size; attribute CSSOMString max-inline-size; attribute CSSOMString placeSelf; attribute CSSOMString alignSelf; attribute CSSOMString justifySelf; attribute CSSOMString place-self; attribute CSSOMString align-self; attribute CSSOMString justify-self; attribute CSSOMString positionAnchor; attribute CSSOMString position-anchor; attribute CSSOMString positionArea; attribute CSSOMString position-area; }; Its name attribute represents the name declared in the rule's prelude. Its style attribute represents the properties declared in the rule's body, in the specified order. On getting, it must return a {{CSSPositionTryDescriptors}} object for the ''@position-try'' at-rule, with the following properties: : [=CSSStyleDeclaration/computed flag=] :: Unset : [=CSSStyleDeclaration/readonly flag=] :: Unset : [=CSSStyleDeclaration/declarations=] :: The declared descriptors in the rule, in [=specified order=]. : [=CSSStyleDeclaration/parent CSS rule=] :: The context object : [=CSSStyleDeclaration/owner node=] :: Null

Appendix: Style & Layout Interleaving

Style & layout interleaving is a technique where a style update can occur on a subtree during the layout process, resulting in retroactive updates to elements’ [=computed value|computed styles=]. Issue: This is not the correct spec for this concept, it should probably go in Cascade, but I need a sketch of it to refer to. Note: [=Style & layout interleaving=] is already used with [=container queries=] and [=container query lengths=]. A length like ''10cqw'' is resolved into a [=computed length=] using layout information about the query container's size, which can thus trigger transitions when the container changes size between layouts. The [=accepted @position-try properties=] are also [=interleaved=] when resolving fallback (see 'position-try'). Issue: Obviously this needs way more details filled in, but for now "act like you already do for container queries" suffices. That behavior is also undefined, but at least it's interoperable (to some extent?). Security Considerations {#sec} ======================= No Security issues have been raised against this document. Privacy Considerations {#priv} ====================== No Privacy issues have been raised against this document. Changes {#changes} ======= Significant changes since the 26 March 2024 Working Draft: * Renamed inset-area to 'position-area' * Added ''position-anchor: auto'' to use the [=implicit anchor element=] as the [=default anchor element=] * Added top-layer details to the [=acceptable anchor element=] definition * Removed the implicit keyword from the [=anchor functions=] (omitting an anchor name does the same thing) * Removed the "reduce the size of the available space" behavior from ''anchor-center'', now that Align defines how to shift to avoid overflow. * Added 'position-visibility', to hide the positioned element in some cases. * Added a11y guidance ([[#accessibility]]) * Expanded the set of properties that ''anchor-size()'' is used in (to all the relevant [=accepted @position-try properties=]). * Renamed position-try-options to 'position-try-fallbacks' * Removed the inset-area() function from 'position-try-fallbacks' (you just use a 'position-area' value directly now). * Defined how to [=swap due to a try-tactic=]. * Expanded the details of how to [=determine position fallback styles=].