CSS Anchor Positioning

W3C First Public Working Draft,

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

Abstract

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

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

Status of this document

This section describes the status of this document at the time of its publication. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at https://www.w3.org/TR/.

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

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

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

This document is governed by the 2 November 2021 W3C Process Document.

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

1. Introduction

CSS absolute positioning allows authors to place elements anywhere on the page, without regard to the layout of other elements besides their containing block. This flexibility can be very useful, but also very limiting—often you want to position relative to some other element. Anchor positioning (via the anchor functions anchor() and anchor-size()) allows authors to achieve this, "anchoring" an absolutely-positioned element to one or more other elements on the page, 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. */
  anchor-default: --tooltip;

  /* Align the tooltip’s bottom to the top of the anchor,
     but automatically swap if this overflows the window
     to the tooltip’s top aligns to the anchor’s bottom
     instead. */
  bottom: anchor(auto);

  /* Set up a 300px-wide area, centered on the anchor.
     If centering would put part of it off-screen,
     instead clamp it to remain on-screen. */
  left: clamp(0px, anchor(center) - 150px, 100% - 300px);
  right: clamp(0px, anchor(center) - 150px, 100% - 300px);
  max-width: 300px;

  /* Center the tooltip in that area. */
  justify-self: center;
}

2. Determining The Anchor

2.1. Creating An Anchor: the anchor-name property

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

The anchor-name property declares that an element is an anchor element, and gives it an anchor name to be targeted by. Values are defined as follows:

none

The property has no effect.

<dashed-ident>

If the element generates a principal box, the element is an anchor element, with an anchor name equal to the <dashed-ident>. The 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 anchor elements for a given positioned element, so a name can be reused in multiple places if the usages are scoped appropriately. If there are still multiple valid anchor elements with the given anchor name, the last one is chosen.

2.1.1. Implicit Anchor Elements

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

For example, the Popover API allows a popover to declare what element is anchoring it. This makes the declared element the implicit anchor element for the popover.

Implicit anchor elements can be referenced with the implicit keyword, rather than referring to some anchor-name value.

2.2. Finding An Anchor

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

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 target anchor element for query el given the query el’s default anchor specifier.

  2. If anchor spec is implicit, and 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.

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

    If no element satisfies these conditions, return nothing.

Note: The general rule captured by these conditions is that el must be fully laid out before query el is laid out. CSS’s rules about the layout order of stacking contexts give us assurances about this, and the list of conditions above exactly rephrases the stacking context rules into just what’s relevant for this purpose, ensuring there is no possibly circularity in anchor positioning.

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 el is a acceptable anchor element for an absolutely positioned element query el if all of the following are true:

2.3. Default Anchors: the anchor-default property

Name: anchor-default
Value: <anchor-element>
Initial: implicit
Applies to: absolutely positioned elements
Inherited: no
Percentages: n/a
Computed value: as specified
Canonical order: per grammar
Animation type: discrete

The anchor-default property defines the default anchor specifier for all anchor functions on the element, allowing multiple elements to use the same set of anchor functions (and position fallback lists!) while changing which anchor element each is referring to.

Its values are identical to the <anchor-element> term in anchor() and anchor-size().

For example, in the following code both .foo and .bar elements can use the same positioning properties and fallback, just changing the anchor element they’re referring to:
.anchored {
  position: absolute;
  position-fallback: --under-then-over;
}

@position-fallback --under-then-over {
  @try {
    // No  specified,
    // so it takes from 'anchor-default'.
    top: calc(.5em + anchor(auto));
    bottom: auto;
  }
}

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

3. Anchor-Based Positioning

An absolutely-positioned element can use the anchor() function in its inset properties to refer to the position of one or more anchor elements. The anchor() function resolves to a <length>, exactly what is needed to position the given inset edge to the specified position on the anchor element.

3.1. The anchor() Function

An absolutely-positioned element can use the anchor() function as a value in its inset properties to refer to the position of one or more anchor elements. The anchor() function resolves to a <length>.

<anchor()> = anchor( <anchor-element>? <anchor-side>, <length-percentage>? )
<anchor-element> = <dashed-ident> | implicit
<anchor-side> = auto | auto-same
           | top | left | right | bottom
           | start | end | self-start | self-end
           | <percentage> | center

The anchor() function has three arguments:

Computed value for anchor() probably needs to be the anchor() function, but with the target anchor element resolved. This allows for transitions to work properly with tree-scoped names, and with changing anchor elements. See Issue 8180.

An anchor() function representing a valid anchor function resolves at used value time to the <length> that would align the edge of the positioned elements' 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 element’s containing block are scrolled to their initial scroll position (but see anchor-scroll).

If the target anchor element is fragmented, the axis-aligned bounding rectangle of the fragments' border boxes is used instead.

Do we need to control which box we’re referring to, so you can align to padding or content edge?

If the positioned element has a snapshotted scroll offset, then it is additionally visually shifted by those offsets, as if by an additional translate() transform.

For example, in .bar { top: anchor(--foo top); }, the anchor() will resolve to the length that’ll line up the .bar element’s top edge with the --foo anchor’s top edge.

On the other hand, in .bar { bottom: anchor(--foo top); }, it will instead resolve to the length that’ll line up the .bar element’s bottom edge with the --foo anchor’s top edge.

Since top and bottom values specify insets from different edges (the top and bottom 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 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 element 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.

3.1.1. Automatic Anchor Positioning

The auto and auto-same <anchor-side> keywords indicate the element wants to use automatic anchor positioning in that property’s axis. This has two effects:

For example, to position and size an element to exactly cover the target element:
.cover {
  inset: anchor(auto-same);
}

is equivalent to

.cover {
  top: anchor(top);
  right: anchor(right);
  bottom: anchor(bottom);
  left: anchor(left);
}
When the opposite axis is auto, the element automatically gains fallback behavior. For example:
.tooltip {
  position: fixed;
  anchor-default: --target;
  top: auto; /* initial value */
  bottom: calc(anchor(auto) + .3em);
}

With the above code, the tooltip will default to positioning its bottom edge slightly away from the top edge of its anchor element, hovering just above it; but if that would make it overflow the top edge of the screen (aka the top of its inset-modified containing block, which is the viewport in this case), it will automatically flip to the opposite side, as if you’d instead specified:

.tooltip {
  position: fixed;
  position-fallback: --top-then-bottom;
  anchor-default: --target;
}
@position-fallback --top-then-bottom {
  @try {
    top: auto;
    bottom: calc(anchor(top) + .3em);
  }
  @try {
    top: calc(anchor(bottom) + .3em);
    bottom: auto;
  }
}

If both axises trigger this behavior, it effectively gains four fallbacks, trying each combination of specified and opposing anchors to find one that won’t trigger overflow.

3.2. Taking Scroll Into Account: the anchor-scroll property

Name: anchor-scroll
Value: none | default | <anchor-element>
Initial: default
Applies to: absolutely-positioned elements
Inherited: no
Percentages: n/a
Computed value: as specified
Canonical order: per grammar
Animation type: discrete

Because scrolling is often done in a separate thread from layout in implementations for performance reasons, but anchor() can result in both positioning changes (which can be handled in the scrolling thread) and layout changes (which cannot), anchor() is defined to assume all the scroll containers between the anchor element and the positioned element’s containing block are at their initial scroll position. This means a positioned element will not be aligned with its anchor if any of the scrollers are not at their initial positions.

The anchor-scroll property allows an author to compensate for this, without losing the performance benefits of the separate scrolling thread, so long as the positioned element is only anchoring to a single anchor element. Its values are:

none

No effect.

default

Behaves identically to <anchor-element>, but draws its value from anchor-default on the element.

<anchor-element>

Selects a target anchor element the same as anchor(), which will be compensated for in positioning and fallback.

Note: When the element uses anchor-default or has an implicit anchor element, authors can often avoid explicitly setting an anchor-scroll value because the initial value is default.

If anchor-scroll is not none on an absolutely-positioned element query el, and there is a target anchor element for query el given the anchor-scroll value, and at least one anchor() function on query el refers to the same target anchor element, then query el has a snapshotted scroll offset, which is a pair of lengths representing a vertical and horizontal offset.

The snapshotted scroll offset is the sum of the offsets from the initial scroll position of all scroll container ancestors of the target anchor element, up to but not including query el’s containing block.

Define the precise timing of the snapshot: updated each frame, before style recalc.

If query el has an additional fallback-bounds rect, similarly calculate the sum of the offsets from the initial scroll position of all scroll container ancestors of the element generating the additional fallback-bounds rect, and subtract that summed offset from the additional fallback-bounds rect’s position.

3.3. Validity

An anchor() function is a valid anchor function only if all the following conditions are true:

If any of these conditions are false, the anchor() function resolves to its specified fallback value. If no fallback value is specified, it resolves to 0px.

4. Anchor-based Sizing

An absolutely-positioned element can use the anchor-size() function in its sizing properties to refer to the size of one or more anchor elements. The anchor-size() function resolves to a <length>.

4.1. The anchor-size() Function

anchor-size() = anchor( <anchor-element>? <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 element (for self-block and self-inline) or the writing mode of the element’s containing block (for block and inline).

An anchor-size() function representing a valid anchor-size function resolves 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.2. Validity

An anchor-size() function is a valid anchor-size function only if all the following conditions are true:

If any of these conditions are false, the anchor-size() function resolves to its specified fallback value. If no fallback value is specified, it resolves to 0px.

5. Fallback Sizing/Positioning

Anchor positioning, while powerful, can also be unpredictable. The anchor element might be anywhere on the page, so positioning an element in any particular fashion (such as above the anchor, or the right of the anchor) might result in the positioned element overflowing its containing block or being positioned partially off screen.

To ameliorate this, an absolutely positioned element can use the position-fallback property to refer to a @position-fallback block, giving a list of possible style rules to try out. Each is applied to the element, one by one, and the first that doesn’t cause the element to overflow its containing block is taken as the winner.

5.1. The position-fallback Property

Name: position-fallback
Value: none | <dashed-ident>
Initial: none
Applies to: absolutely-positioned elements
Inherited: no
Percentages: n/a
Computed value: as specified
Canonical order: per grammar
Animation type: discrete

Values have the following meanings:

none

The property has no effect; the element does not use a position fallback list.

<dashed-ident>

If there is a @position-fallback rule with a name matching the specified ident, then the element uses that position fallback list.

Otherwise, this value has no effect.

5.2. The @position-fallback Rule

The @position-fallback rule defines a position fallback list with a given name, specifying one or more sets of positioning properties inside of @try blocks that will be applied to an element, with each successive one serving as fallback if the previous would cause the element to partially or fully overflow its containing block.

The grammar of the @position-fallback rule is:

@position-fallback <dashed-ident> {
  <rule-list>
}

@try { <declaration-list> }

The @position-fallback rule only accepts @try rules. The <dashed-ident> specified in the prelude is the rule’s name. If multiple @position-fallback rules are declared with the same name, the last one in document order "wins".

The @try rule only accepts the following properties:

What exactly are the constraints that determine what’s allowed here? Current list is based off of what’s reasonable from Chrome’s experimental impl. We can make a CQ that keys off of which fallback was used to allow more general styling, at least for descendants.

The @try rules inside a @position-fallback specify a position fallback list, where each entry consists of the properties specified by each @try, in order.

Would be useful to be able to detect when your anchor(s) are fully off-screen and suppress your display entirely. For example, tooltips living outside the scroller holding the text they’re anchored to don’t want to just hover over arbitrary parts of the page because their anchor happens to have that position relative to the scrollport.

Note: If multiple elements using different anchors want to use the same fallback positioning, just relative to their own anchor elements, omit the <anchor-element> in anchor() and specify each element’s anchor in anchor-default instead.

Note: The most common types of fallback positioning (putting the positioned element on one side of the anchor normally, but flipping to the opposite side if needed) can be done automatically, without using @position-fallback at all, by using auto or auto-side side values in the anchor() function.

5.3. Applying Stronger Fallback Bounds: the position-fallback-bounds property

When an element using anchor positioning is using position: absolute, it determines whether or not it’s overflowing (and thus should try a different fallback position) by looking at its (scroll-adjusted) inset-modified containing block. By carefully selecting where in the DOM the positioned element lives, and what element establishes its containing block, you can choose a useful element to use for its overflow bounds.

When using position: fixed, or things like the Popover API that use the top layer, you lose this ability; their containing block is always the viewport or the root element’s containing block. The position-fallback-bounds property restores this ability, allowing an element to explicitly select what element it wants to use for checking overflow against.

Name: position-fallback-bounds
Value: normal | <dashed-ident>
Initial: normal
Applies to: absolutely positioned elements
Inherited: no
Percentages: n/a
Computed value: as specified
Canonical order: per grammar
Animation type: discrete
normal

The element uses its normal (scroll-adjusted, inset-modified) containing block to determine if it’s overflowing for the purpose of selecting a position fallback list entry.

<dashed-ident>

In addition to checking overflow against its containing block, as per normal, the element checks against its additional fallback-bounds rect.

The additional fallback-bounds rect is the padding box of the target anchor element given this element and the specified <dashed-ident>. If there is no such target anchor element, there is no additional fallback-bounds rect.

padding-box? Or content-box? Should it be controllable?

5.4. Applying Position Fallback

When an element uses a position fallback list, it selects one entry from the list as defined below, and applies those properties to itself as used values.

Note: These have to be applied as used values because we’re in the middle of layout right now; defining how they’d interact with the cascade would be extremely confusing *at a minimum*, and perhaps actually circular. In any case, not worth the cost in spec or impl.

This implies that the values can’t be transitioned in the usual fashion, since transitions key off of computed values and we’re past that point. However, popovers sliding between positions is a common effect in UI libs. Probably should introduce a smooth keyword to position-fallback to trigger automatic "animation" of the fallback’d properties.

To determine the position fallback styles of an element el:
  1. Let base styles be the current used styles of el.

  2. For each fallback styles in the position fallback list:

    1. Apply the styles in fallback styles to el, overriding the corresponding properties in base styles.

      Perform any specified/computed/used-value time normalizations that are required to make the overridden styles into used values (such as resolving math functions, etc).

      Let adjusted styles be el’s styles after these adjustments.

    2. If el has a snapshotted scroll offset, then subtract the offsets from el’s margin box’s position.

      Also, if any of el’s inset properties are non-auto, subtract the snapshotted scroll offset for the appropriate axis from their values. Recalculate el’s inset-modified containing block using these shifted values to obtain the scroll-adjusted IMCB.

    3. If el’s margin box is not fully contained within the scroll-adjusted IMCB, continue.

    4. If el has an additional fallback-bounds rect, and el’s margin box is not fully contained within it, continue.

    5. Use adjusted styles for el and exit this algorithm.

  3. If the previous step finished without selecting a set of styles, use the adjusted styles corresponding to the final entry in the position fallback list.

Note: Descendants overflowing el don’t affect this calculation, only el’s own margin box.

The styles returned by determining the position fallback styles are taken as the final values for the specified properties.

Implementations may choose to impose an implementation-defined limit on the length of position fallback lists, to limit the amount of excess layout work that may be required. This limit must be at least five.

Nested anchors (an anchored element inside of another anchored element) present the potential for exponential blow-up of layouts when doing fallback, since the grandchild anchored element can cause scrollbars on an ancestor, changing the IMCB for the child anchored element, thus possibly causing the fallback choice to change for it.

There are strategies to avoid this, but they’re not without costs of their own. We should probably impose a maximum limit as well, to avoid this.

However, since *most* usages won’t be problematic in the first place, we don’t want to restrict them unduly just to prevent weird situations from exploding. Perhaps a complexity budget based on the branching factor at each level? Like, accumulate the product of the fallback list lengths from ancestors, and your fallback list gets limited to not exceed a total product of, say, 1k. Get too deep and you’re stuck with your first choice only! But this would allow large, complex fallback lists for top-level stuff, and even some reasonable nesting. (Length-five lists could be nested to depth of 4, for example, if we did go with 1k.)

More thought is needed.

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;
  position-fallback: --button-popover;
  overflow: auto;

  /* 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;
}

@position-fallback --button-popover {
  /* First try to align the top, left edge of the popover
  with the bottom, left edge of the button. */
  @try {
    top: anchor(--button bottom);
    left: anchor(--button left);
  }

  /* Next try to align the bottom, left edge of the popover
  with the top, left edge of the button. */
  @try {
    bottom: anchor(--button top);
    left: anchor(--button left);
  }

  /* Next try to align the top, right edge of the popover
  with the bottom, right edge of the button. */
  @try {
    top: anchor(--button bottom);
    right: anchor(--button right);
  }

  /* Finally, try to align the bottom, right edge of the popover
  with the top, right edge of the button. Other positions are possible,
  but this is the final option the author would like the rendering
  engine to try. */
  @try {
    bottom: anchor(--button top);
    right: anchor(--button right);
  }
}

5.5. Fallback and Automatic Positioning

When an element uses an anchor() function with an auto or auto-same <anchor-side> argument in an inset property, and the opposite inset property is auto, the element is said to be trying to use automatic anchor fallbacks in that axis.

If the element has position-fallback: none, and is trying to use automatic anchor fallbacks in one axis, it automatically generates a position fallback list consisting of two entries:

If the element uses automatic anchor positioning in both axises, it instead adds four entries to the position fallback list: one specifying the base styles, as above, then one reversing just the block axis, followed by one reversing just the inline axis, followed by one reversing both axises at once.

Note: If the element has a non-none position-fallback, these automatic fallbacks aren’t generated. Since the position fallback list styles override the "base" styles immediately, this will usually mean you wouldn’t see a "base" anchor(auto) show up in the final styles at all, but if that does happen (it’s specified in a property that isn’t overriden by anything in the position fallback list), the only effect of the auto/auto-same is to resolve to the appropriate side keyword.

Automatic anchor fallback can also be used as a shorthand in @try blocks.

If applying an entry in the element’s position fallback list would cause the resulting styles to satisfy the conditions of automatic anchor fallbacks, and the relevant anchor() function comes from a @try block (rather than from the base styles), then that entry of the position fallback list must instead be treated as 2 or 4 consecutive entries, generated as above.

(Otherwise, the auto or auto-same keywords just resolve to the appropriate side, with no additional effects.)

For example, the following @position-fallback rule:
@position-fallback --compact {
  @try {
    top: anchor(auto);
    bottom: auto;
  }
}

is equivalent to the following longer, more explicit rule:

@position-fallback --expanded {
  @try {
    top: anchor(bottom);
    bottom: auto;
  }
  @try {
    top: auto;
    bottom: anchor(top);
  }
}

6. Security Considerations

No Security issues have been raised against this document.

7. Privacy Considerations

No Privacy issues have been raised against this document.

Conformance

Document conventions

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:

This is an example of an informative example.

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.

Conformance classes

Conformance to this specification is defined for three conformance classes:

style sheet
A CSS style sheet.
renderer
A UA that interprets the semantics of a style sheet and renders documents that use them.
authoring tool
A UA that writes a style sheet.

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.

Partial implementations

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.

Implementations of Unstable and Proprietary Features

To avoid clashes with future stable CSS features, the CSSWG recommends following best practices for the implementation of unstable features and proprietary extensions to CSS.

Non-experimental implementations

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.

Further information on submitting testcases and implementation reports can be found from on the CSS Working Group’s website at https://www.w3.org/Style/CSS/Test/. Questions should be directed to the public-css-testsuite@w3.org mailing list.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[CSS-ALIGN-3]
Elika Etemad; Tab Atkins Jr.. CSS Box Alignment Module Level 3. 17 February 2023. WD. URL: https://www.w3.org/TR/css-align-3/
[CSS-BOX-4]
Elika Etemad. CSS Box Model Module Level 4. 3 November 2022. WD. URL: https://www.w3.org/TR/css-box-4/
[CSS-BREAK-4]
Rossen Atanassov; Elika Etemad. CSS Fragmentation Module Level 4. 18 December 2018. WD. URL: https://www.w3.org/TR/css-break-4/
[CSS-CASCADE-5]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 5. 13 January 2022. CR. URL: https://www.w3.org/TR/css-cascade-5/
[CSS-DISPLAY-3]
Elika Etemad; Tab Atkins Jr.. CSS Display Module Level 3. 30 March 2023. CR. URL: https://www.w3.org/TR/css-display-3/
[CSS-LOGICAL-1]
Rossen Atanassov; Elika Etemad. CSS Logical Properties and Values Level 1. 27 August 2018. WD. URL: https://www.w3.org/TR/css-logical-1/
[CSS-OVERFLOW-3]
Elika Etemad; Florian Rivoal. CSS Overflow Module Level 3. 29 March 2023. WD. URL: https://www.w3.org/TR/css-overflow-3/
[CSS-POSITION-3]
Elika Etemad; Tab Atkins Jr.. CSS Positioned Layout Module Level 3. 3 April 2023. WD. URL: https://www.w3.org/TR/css-position-3/
[CSS-POSITION-4]
CSS Positioned Layout Module Level 4 URL: https://drafts.csswg.org/css-position-4/
[CSS-SCOPING-1]
Tab Atkins Jr.; Elika Etemad. CSS Scoping Module Level 1. 3 April 2014. WD. URL: https://www.w3.org/TR/css-scoping-1/
[CSS-SIZING-3]
Tab Atkins Jr.; Elika Etemad. CSS Box Sizing Module Level 3. 17 December 2021. WD. URL: https://www.w3.org/TR/css-sizing-3/
[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. 24 December 2021. CR. URL: https://www.w3.org/TR/css-syntax-3/
[CSS-TRANSFORMS-1]
Simon Fraser; et al. CSS Transforms Module Level 1. 14 February 2019. CR. URL: https://www.w3.org/TR/css-transforms-1/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 4. 6 April 2023. WD. URL: https://www.w3.org/TR/css-values-4/
[CSS-WRITING-MODES-4]
Elika Etemad; Koji Ishii. CSS Writing Modes Level 4. 30 July 2019. CR. URL: https://www.w3.org/TR/css-writing-modes-4/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119

Informative References

[CSS-SHADOW-PARTS-1]
Tab Atkins Jr.; Fergal Daly. CSS Shadow Parts. 15 November 2018. WD. URL: https://www.w3.org/TR/css-shadow-parts-1/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/

Property Index

Name Value Initial Applies to Inh. %ages Anim­ation type Canonical order Com­puted value
anchor-default <anchor-element> implicit absolutely positioned elements no n/a discrete per grammar as specified
anchor-name none | <dashed-ident> none all elements that generate a principal box no n/a discrete per grammar as specified
anchor-scroll none | default | <anchor-element> default absolutely-positioned elements no n/a discrete per grammar as specified
position-fallback none | <dashed-ident> none absolutely-positioned elements no n/a discrete per grammar as specified
position-fallback-bounds normal | <dashed-ident> normal absolutely positioned elements no n/a discrete per grammar as specified

Issues Index

Computed value for anchor() probably needs to be the anchor() function, but with the target anchor element resolved. This allows for transitions to work properly with tree-scoped names, and with changing anchor elements. See Issue 8180.
Do we need to control which box we’re referring to, so you can align to padding or content edge?
Define the precise timing of the snapshot: updated each frame, before style recalc.
What exactly are the constraints that determine what’s allowed here? Current list is based off of what’s reasonable from Chrome’s experimental impl. We can make a CQ that keys off of which fallback was used to allow more general styling, at least for descendants.
Would be useful to be able to detect when your anchor(s) are fully off-screen and suppress your display entirely. For example, tooltips living outside the scroller holding the text they’re anchored to don’t want to just hover over arbitrary parts of the page because their anchor happens to have that position relative to the scrollport.
padding-box? Or content-box? Should it be controllable?
This implies that the values can’t be transitioned in the usual fashion, since transitions key off of computed values and we’re past that point. However, popovers sliding between positions is a common effect in UI libs. Probably should introduce a smooth keyword to position-fallback to trigger automatic "animation" of the fallback’d properties.
Nested anchors (an anchored element inside of another anchored element) present the potential for exponential blow-up of layouts when doing fallback, since the grandchild anchored element can cause scrollbars on an ancestor, changing the IMCB for the child anchored element, thus possibly causing the fallback choice to change for it.

There are strategies to avoid this, but they’re not without costs of their own. We should probably impose a maximum limit as well, to avoid this.

However, since *most* usages won’t be problematic in the first place, we don’t want to restrict them unduly just to prevent weird situations from exploding. Perhaps a complexity budget based on the branching factor at each level? Like, accumulate the product of the fallback list lengths from ancestors, and your fallback list gets limited to not exceed a total product of, say, 1k. Get too deep and you’re stuck with your first choice only! But this would allow large, complex fallback lists for top-level stuff, and even some reasonable nesting. (Length-five lists could be nested to depth of 4, for example, if we did go with 1k.)

More thought is needed.