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; }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 propertyThe 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:
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 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.
2.1.1. Implicit Anchor ElementsSome 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’s PositionSeveral 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.
2.2. Scoping Anchor Names: the anchor-scope propertyThis 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:
This value only affects anchor names in the same tree scope, as if it were a tree-scoped name. (That is, anchor-scope: all acts identically to anchor-scope: --foo, --bar, ..., listing all relevant anchor names.)
Note: Per [css-scoping-1], when two tree-scoped names are compared, they only match if they’re from the same tree.
This property has no effect on implicit anchor elements.
When a design pattern is re-used,
anchor-scopecan 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.
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 elementgiven a querying element
query eland an optional
anchor specifier anchor spec:
If anchor spec was not passed, return the default anchor element if it exists, otherwise return nothing.
If anchor spec is auto:
If the Popover API defines an implicit anchor element for query el which is an acceptable anchor element for query el, 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.)
An element
possible anchoris an
acceptable anchor elementfor an
absolutely positionedelement
positioned elif 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, and the last containing block in possible anchor’s containing block chain before reaching the initial containing block is either not absolutely positioned or precedes positioned el in the tree order.
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, and the last containing block in possible anchor’s containing block chain before reaching positioned el’s containing block is either not absolutely positioned or precedes positioned el in the tree order.
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.
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.
Use the implicit anchor element if it exists; otherwise the box has no default anchor element.
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
.fooand
.barelements 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 PositioningAn 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 PropertyMost 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.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, 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 § 3.1.4 Area-specific Default Alignment.
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> ValuesPositions 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 | 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} ]
The <position-area> value selects a region of the position-area grid by specifying the rows and columns the region occupies as follows:
The single corresponding row or column, depending on which axis this keyword is specifying.
Like in anchor(), the plain logical keywords (start, end, etc) refer to the writing mode of the box’s containing block. The x-start/etc determine their direction in the same way, but in the specified physical axis.
The self-* logical keywords (self-start, x-self-end, etc) are identical, but refer to the box’s own writing mode.
Two adjacent 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.)
All three rows or columns, depending on which axis this keyword is specifying.
Some keywords are ambiguous about what axis they refer to: center, span-all, and the 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 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 span-all if the given keyword is unambigous about its axis; otherwise, it behaves as if the given keyword was repeated. (For example, top is equivalent to top span-all, but center is equivalent to center center.)
3.1.3. Computed Value and Serialization of <position-area>The computed value of a <position-area> value is the two keywords indicating the selected tracks in each axis, with the long (block-start) and short (start) logical keywords treated as equivalent. It serializes in the order given in the grammar (above), with the logical keywords serialized in their short forms (e.g. start start instead of block-start inline-start).
3.1.4. Area-specific Default AlignmentEach <position-area> also implies a default self-alignment, which will be used if the self-alignment property on the box is normal:
If the only the center track in an axis is selected, the default alignment in that axis is center.
If all three tracks are selected, the default alignment in that axis is anchor-center.
Otherwise, the default alignment in that axis is toward the non-specified side track: if it’s specifying the “start” track of its axis, the default alignment in that axis is end; etc.
For example, assuming an English-equivalent writing mode (horizontal-tb, ltr), then the value
span-x-start topresolves to the
startregion of the vertical axis, and the
startand
centerregions of the horizontal axis, so the default alignments will be
align-self: end(making the box’s bottom
margin edgeflush with the bottom of the
topregion) and
justify-self: end(making the box’s end-side
margin edgeflush with the end side of the
span-startregion).
3.2. Centering on the Anchor: the anchor-center alignment valueThe 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 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: When using anchor-center, by default if the anchor is too close to the edge of the box’s original containing block, it will “shift” from being purely centered, in order to remain within the original containing block. See CSS Box Alignment 3 § 4.4 Overflow Alignment: the safe and unsafe keywords and scroll safety limits for more details.
3.3. Anchor-relative Insets: the anchor() functionAn 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 <length>. It is only allowed in the inset properties (and is otherwise invalid).
<anchor()> = anchor( <anchor-name>? && <anchor-side>, <length-percentage>? ) <anchor-name> = <dashed-ident> <anchor-side> = inside | outside | top | left | right | bottom | start | end | self-start | self-end | <percentage> | center
The anchor() function has three arguments:
the <anchor-name> 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.
Selects the default anchor element defined for the box, if possible.
See target anchor element for details.
the <anchor-side> value refers to the position of the corresponding side of the target anchor element. Its possible values are:
Resolves to one of the anchor box’s sides, depending on which inset property it’s used in. inside refers to the same side as the inset property (attaching the positioned box to the "inside" of the anchor box), while outside refers to the opposite.
Refers to the specified side of the anchor box.
Note: These are only usable in the inset properties in the matching axis. For example, left is usable in left, right, or the logical inset properties that refer to the horizontal axis.
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 self-start and self-end) or the positioned box’s containing block (for start and end).
Refers to a position a corresponding percentage between the start and end sides, with 0% being equivalent to start and 100% being equivalent to end.
center is equivalent to 50%.
the optional <length-percentage> final argument is a fallback value, specifying what the function should compute to if it’s an unresolvable anchor function.
An anchor() function representing a resolvable anchor function resolves at computed value time (using style & layout interleaving) to the <length> 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 § 3.4 Taking Scroll Into Account).
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 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
--fooanchor’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
<length>, it can be used in
math functionslike 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.
An anchor() function is a resolvable anchor function only if all the following conditions are true:
It’s applied to an absolutely positioned box.
If its <anchor-side> specifies a physical keyword, it’s specified in an inset property applicable to that axis. (For example, left can only be used in left, right, or a logical inset property in the horizontal axis.)
There is a target anchor element for the box it’s used on, and the <anchor-name> value specified in the function.
If any of these conditions are false, the anchor() function computes to its specified fallback value. If no fallback value is specified, it makes the declaration referencing it invalid at computed-value time.
3.4. Taking Scroll Into AccountFor 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 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 pointoccurs for an
absolutely positioned elementwhenever that element begins generating boxes (aka switches from
display:noneor
display:contentsto any other
displayvalue), 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.
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 scrollin 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-none value for position-area
at least one anchor() function on abspos’s 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).
Define the precise timing of the snapshot: updated each frame, before style recalc.
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.
3.5. Conditional Hiding: the position-visibility propertyThere 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.
This property has no effect. (The box is displayed without regard for its anchors or its overflowing status.)
If any of the box’s required anchor references do not resolve to a target anchor element, the box’s visibility property computes to force-hidden.
What is a required anchor reference? anchor() functions that don’t have a fallback value; the default anchor *sometimes*? Need more detail here.
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?
If the box has a default anchor box but that anchor box is invisible or clipped by intervening boxes, the box’s visibility property computes to force-hidden.
If the box overflows its inset-modified containing block even after applying position-try, the box’s visibility property computes to force-hidden.
An anchor box
anchoris
clipped by intervening boxesrelative to a positioned box
absposrelying on it if
anchor’s
ink overflow rectangleis fully clipped by a box which is an ancestor of
anchorbut 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.
Make sure this definition of clipped is consistent with View Transitions, which wants a similar concept.
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.
4. Anchor-Based SizingAn 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 <length>. It is only allowed in the accepted @position-try properties (and is otherwise invalid).
4.1. The anchor-size() Function 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() = anchor-size( [ <anchor-name> || <anchor-size> ]? , <length-percentage>? ) <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 <anchor-side> keywords are replaced with <anchor-size>, referring to the distance between two opposing sides.
The physical <anchor-size> 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 <anchor-size> 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 block and inline).
If the <anchor-size> 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 resolvable anchor-size function resolves at computed value time (via style & layout interleaving) to the <length> separating the relevant border edges (either left and right, or top and bottom, whichever is in the specified axis) of the target anchor element.
4.1.1. Resolution of anchor-size()An anchor-size() function is a resolvable anchor-size function only if all the following conditions are true:
It’s applied to an absolutely positioned box.
There is a target anchor element for the box it’s used on, and the <anchor-name> 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.
5. Overflow ManagementAnchor 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.
5.1. Giving Fallback Options: the position-try-fallbacks propertyThis 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 <try-tactic> representing an automatic transformation of the box’s existing computed style.
Values have the following meanings:
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.
<try-tactic> = flip-block || flip-inline || flip-start
swaps the values in the block axis (between, for example, margin-block-start and margin-block-end), essentially mirroring across an inline-axis line.
swaps the values in the inline axis, essentially mirroring across a block-axis line.
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 start-start corner to the 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 <try-tactic> and adds the result to the box’s position options list.
Otherwise, does nothing.
Automatically creates a position option composed solely of a position-area property with the given value.
To
swap due to a try-tacticthe styles of a box’s element
elbetween two directions
directions, returning a set of styles:
If directions are opposites along the same axis, they are “opposing”. Otherwise (when they are specifying different axises), they are “perpendicular”.
Determine the specified values of the accepted @position-try properties on el, and let styles be the result.
Substitute variables, env() functions, and similar arbitrary substitution functions in styles.
For env() functions, if the referenced environment variable is associated with a direction or axis (such as safe-area-inset-top), switch the referenced environment variable corresponding to directions.
For example, if
top: env(safe-area-inset-top);is specified, and
directionsare 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
leftproperty.)
Swap the values of the styles between the associated properties corresponding to directions.
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.
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 <percentage> is used, and directions are opposing, change it to 100% minus the original percentage.
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 the self-alignment properties, if directions are opposing, change the specified <self-position> (or left/right keywords), if any, to maintain the same relative relationship to the new direction that they had to the old.
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.
Return styles.
This property specifies the order in which the position options list will be tried.
<try-size> = most-width | most-height | most-block-size | most-inline-size
Try the position options in the order specified by position-try-fallbacks.
For each entry in the position options list, 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.
5.3. The position-try ShorthandThis 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.
5.4. The @position-try RuleThe @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 <dashed-ident> { <declaration-list> }
The <dashed-ident> 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:
It is invalid to use !important on the properties in the <declaration-list>. 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 <anchor-name> 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.
5.5. Applying Position FallbackWhen 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
determine position fallback stylesfor an element
abspos:
Let current styles be the current used styles of abspos (which might be the result of earlier fallback).
For each option in the position options list:
If option is currently abspos’s last successful position option, continue.
Let adjusted styles be the result of applying a position option option to abspos.
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.
If cb rect was negative-size in either axis and corrected into zero-size, 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.
If el rect is not fully contained within cb rect, continue.
Return adjusted styles, along with the associated set of remembered scroll offsets that were hypothetically calculated for them.
Assert: The previous step finished without finding a position option that avoids overflow.
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 notcause 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.
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; }6. Accessibility Implications
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 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.
The CSSPositionTryRule
interface represents the @position-try rule:
[Exposed=Window] interfaceCSSPositionTryRule
: CSSRule { readonly attribute CSSOMString name; [SameObject, PutForwards=cssText] readonly attribute CSSPositionTryDescriptors style; }; [Exposed=Window] interfaceCSSPositionTryDescriptors
: CSSStyleDeclaration { attribute CSSOMStringmargin
; attribute CSSOMStringmarginTop
; attribute CSSOMStringmarginRight
; attribute CSSOMStringmarginBottom
; attribute CSSOMStringmarginLeft
; attribute CSSOMStringmarginBlock
; attribute CSSOMStringmarginBlockStart
; attribute CSSOMStringmarginBlockEnd
; attribute CSSOMStringmarginInline
; attribute CSSOMStringmarginInlineStart
; attribute CSSOMStringmarginInlineEnd
; attribute CSSOMStringmargin-top
; attribute CSSOMStringmargin-right
; attribute CSSOMStringmargin-bottom
; attribute CSSOMStringmargin-left
; attribute CSSOMStringmargin-block
; attribute CSSOMStringmargin-block-start
; attribute CSSOMStringmargin-block-end
; attribute CSSOMStringmargin-inline
; attribute CSSOMStringmargin-inline-start
; attribute CSSOMStringmargin-inline-end
; attribute CSSOMStringinset
; attribute CSSOMStringinsetBlock
; attribute CSSOMStringinsetBlockStart
; attribute CSSOMStringinsetBlockEnd
; attribute CSSOMStringinsetInline
; attribute CSSOMStringinsetInlineStart
; attribute CSSOMStringinsetInlineEnd
; attribute CSSOMStringtop
; attribute CSSOMStringleft
; attribute CSSOMStringright
; attribute CSSOMStringbottom
; attribute CSSOMStringinset-block
; attribute CSSOMStringinset-block-start
; attribute CSSOMStringinset-block-end
; attribute CSSOMStringinset-inline
; attribute CSSOMStringinset-inline-start
; attribute CSSOMStringinset-inline-end
; attribute CSSOMStringwidth
; attribute CSSOMStringminWidth
; attribute CSSOMStringmaxWidth
; attribute CSSOMStringheight
; attribute CSSOMStringminHeight
; attribute CSSOMStringmaxHeight
; attribute CSSOMStringblockSize
; attribute CSSOMStringminBlockSize
; attribute CSSOMStringmaxBlockSize
; attribute CSSOMStringinlineSize
; attribute CSSOMStringminInlineSize
; attribute CSSOMStringmaxInlineSize
; attribute CSSOMStringmin-width
; attribute CSSOMStringmax-width
; attribute CSSOMStringmin-height
; attribute CSSOMStringmax-height
; attribute CSSOMStringblock-size
; attribute CSSOMStringmin-block-size
; attribute CSSOMStringmax-block-size
; attribute CSSOMStringinline-size
; attribute CSSOMStringmin-inline-size
; attribute CSSOMStringmax-inline-size
; attribute CSSOMStringplaceSelf
; attribute CSSOMStringalignSelf
; attribute CSSOMStringjustifySelf
; attribute CSSOMStringplace-self
; attribute CSSOMStringalign-self
; attribute CSSOMStringjustify-self
; attribute CSSOMStringpositionAnchor
; attribute CSSOMStringposition-anchor
; attribute CSSOMStringpositionArea
; attribute CSSOMStringposition-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:
Unset
Unset
The declared descriptors in the rule, in specified order.
The context object
Null
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 styles.
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).
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?).
9. Security ConsiderationsNo Security issues have been raised against this document.
10. Privacy ConsiderationsNo Privacy issues have been raised against this document.
11. ChangesSignificant changes since the 4 March October 2024 Working Draft:
Switched the center track for position-area to default to center alignment. (Issue 11803)
Defined the implicit anchor element of pseudo-elements to be their originating element. (Issue 11792)
Rewrote how scrolling is handled and responded to (§ 3.4 Taking Scroll Into Account), so that the positioned element can respond to its anchors being scrolled when possible (when the positioned element is first rendered, or changes fallbacks). (Issue 10999)
Rebased position-visibility to use the newly defined visibility: force-hidden value instead of creating a custom concept, in order to better enable transitions. (Issue 10182)
Fixed some errors in the anchor resolution algorithm. (Issue 11030, Issue 11170)
Fixed some errors in the list of <position-area> keywords. (Issue 11372, Issue 11371)
Defined that anchor-scope idents are tree-scoped names (and thus compare specially with anchor names). (Issue 10525, Issue 10526)
Clarified that the computed value of position-area does not distinguish long and short forms of logical keywords. (Issue 11828)
Clarified that certain anchor() resolaveability checks are at computed value time, not parse time. (Issue 10955)
Editorial reorganization of sections, particularly § 3.1 The position-area Property.
See also Previous Changes.
Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.
All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]
Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example"
, like this:
Informative notes begin with the word “Note” and are set apart from the normative text with class="note"
, like this:
Note, this is an informative note.
Advisements are normative sections styled to evoke special attention and are set apart from other normative text with <strong class="advisement">
, like this: UAs MUST provide an accessible alternative.
A style sheet is conformant to this specification if all of its statements that use syntax defined in this module are valid according to the generic CSS grammar and the individual grammars of each feature defined in this module.
A renderer is conformant to this specification if, in addition to interpreting the style sheet as defined by the appropriate specifications, it supports all the features defined by this specification by parsing them correctly and rendering the document accordingly. However, the inability of a UA to correctly render a document due to limitations of the device does not make the UA non-conformant. (For example, a UA is not required to render color on a monochrome monitor.)
An authoring tool is conformant to this specification if it writes style sheets that are syntactically correct according to the generic CSS grammar and the individual grammars of each feature in this module, and meet all other conformance requirements of style sheets as described in this module.
So that authors can exploit the forward-compatible parsing rules to assign fallback values, CSS renderers must treat as invalid (and ignore as appropriate) any at-rules, properties, property values, keywords, and other syntactic constructs for which they have no usable level of support. In particular, user agents must not selectively ignore unsupported component values and honor supported values in a single multi-value property declaration: if any value is considered invalid (as unsupported values must be), CSS requires that the entire declaration be ignored.
Once a specification reaches the Candidate Recommendation stage, non-experimental implementations are possible, and implementors should release an unprefixed implementation of any CR-level feature they can demonstrate to be correctly implemented according to spec.
To establish and maintain the interoperability of CSS across implementations, the CSS Working Group requests that non-experimental CSS renderers submit an implementation report (and, if necessary, the testcases used for that implementation report) to the W3C before releasing an unprefixed implementation of any CSS features. Testcases submitted to W3C are subject to review and correction by the CSS Working Group.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4