CSS Shared Element Transitions Module Level 1

Editor’s Draft,

More details about this document
This version:
https://drafts.csswg.org/css-shared-element-transitions-1/
Issue Tracking:
CSSWG Issues Repository
Inline In Spec
Editors:
Tab Atkins-Bittner (Google)
Jake Archibald (Google)
Khushal Sagar (Google)
Suggest an Edit for this Spec:
GitHub Editor

Abstract

This module defines the Single-Page Document-Transition API, along with associated properties and pseudo-elements.

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 is a public copy of the editors’ draft. It is provided for discussion only and may change at any moment. Its publication here does not imply endorsement of its contents by W3C. Don’t cite this document other than as work in progress.

Please send feedback by filing issues in GitHub (preferred), including the spec code “css-shared-element-transitions” in the title, like this: “[css-shared-element-transitions] …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.

1. Introduction

This spec describes the CSS and JS mechanics of the single-page transition API.

2. Transitions as an enhancement

This section is non-normative.

A key part of this API design is the view that an animated transition is an enhancement to a DOM change.

If a transition cannot run, or is skipped, the DOM change should still happen. If the DOM change should also be skipped, then that should be handled by another feature. The `signal` on `NavigateEvent` is an example of a feature developers could use to handle this.

Although the transition API allows DOM changes to be asynchronous via the updateDOM callback, the transition API is not responsible for queuing or otherwise scheduling the DOM changes, beyond the scheduling needed for the transition itself. Some asynchronous DOM changes can happen concurrently (e.g if they’re happening within independent components), whereas others need to queue, or abort an earlier change. This is best left to a feature or framework that has a more holistic view of the update.

3. CSS properties

3.1. page-transition-tag

Name: page-transition-tag
Value: none | <custom-ident>
Initial: none
Applies to: all elements
Inherited: no
Percentages: n/a
Computed value: as specified
Canonical order: per grammar
Animation type: discrete

The page-transition-tag property "tags" an element as participating in a page transition.

none

The element will not participate in a page transition.

<custom-ident>

The element can participate in a page transition, as either an outgoing or incoming element, with a page transition tag equal to the <custom-ident>'s value.

The value none is invalid as a <custom-ident>.

The root element participates in a page transition by default using the following style in the user-agent origin.

html {
  page-transition-tag: root;
}

4. Pseudo-elements

While the UA is animating a page transition, it creates the following page-transition pseudo-elements, to represent the various items being animated.

The ::page-transition pseudo-element acts as a grouping element for other page-transition pseudo-elements and has the document’s root element as its originating element.

For example, :root::page-transition selector matches this pseudo-element, but div::page-transition does not.

Other page-transition pseudo-elements take a <pt-tag-selector> argument to specify which elements tagged with page-transition-tag are affected.

There can be multiple pseudo-elements of the same type, one for each page-transition-tag participating in a transition.

The <pt-tag-selector> is defined as follows:

<pt-tag-selector> = '*' | <custom-ident>

A value of * makes the corresponding selector apply to all pseudo elements of the specified type. The specificity of a page-transition selector with a * argument is zero.

The <custom-ident> value makes the corresponding selector apply to exactly one pseudo element of the specified type, namely the pseudo-element that is created as a result of the page-transition-tag property on an element with the same <custom-ident> value. The specificity of a page-transition selector with a <custom-ident> argument is the same as for other pseudo-elements, and is equivalent to a type selector.

The following describes all of the page-transition pseudo-elements and their function:

::page-transition

This pseudo-element is the grouping container of all the other page-transition pseudo-elements. Its originating element is the document’s root element.

The following user-agent origin styles apply to this element:

html::page-transition {
  position: fixed;
  inset: 0;
}

Note: This pseudo-element provides a containing block for all ::page-transition-container pseudo-elements. The aim of the style is to size the pseudo-element to cover the large viewport size and position all ::page-transition-container pseudo-elements relative to the origin of the large viewport.

::page-transition-container( <pt-tag-selector> )

One of these pseudo-elements exists for each page-transition-tag in a page transition, and holds the rest of the pseudo-elements corresponding to this page-transition-tag.

Its originating element is the ::page-transition pseudo-element.

The following user-agent origin styles apply to this element:

html::page-transition-container(*) {
  position: absolute;
  top: 0;
  left: 0;

  animation-duration: 0.25s;
  animation-fill-mode: both;
}

Note: The aim of the style is to position the element relative to its ::page-transition parent.

In addition to above, styles in the user-agent origin animate this pseudo-element’s width and height from the size of the outgoing element’s border box to that of the incoming element’s border box. Also the element’s transform is animated from the outgoing element’s screen space transform to the incoming element’s screen space transform. This style is generated dynamically since the values of animated properties are determined at the time that the transition begins.

The selector for this and subsequently defined pseudo-elements is likely to change to indicate position in the pseudo-tree hierarchy.

::page-transition-image-wrapper( <pt-tag-selector> )

One of these pseudo-elements exists for each page-transition-tag being in a page transition, and holds the images of the outgoing and incoming elements.

Its originating element is the ::page-transition-container() pseudo-element with the same tag.

The following user-agent origin styles apply to this element:

html::page-transition-image-wrapper(*) {
  position: absolute;
  inset: 0;

  animation-duration: inherit;
  animation-fill-mode: inherit;
}

In addition to above, styles in the user-agent origin add ''isolation: isolate'' to this pseudo-element if it has both ::page-transition-incoming-image and ::page-transition-outgoing-image as descendants.

Note: The aim of the style is to position the element to occupy the same space as its ::page-transition-container element and provide isolation for blending.

Isolation is only necessary to get the right cross-fade between incoming and outgoing image pixels. Would it be simpler to always add it and try to optimize in the implementation?

::page-transition-outgoing-image( <pt-tag-selector> )

One of these pseudo-elements exists for each element in the outgoing DOM being animated by the page transition, and is a replaced element displaying the outgoing element’s snapshot image. It has natural dimensions equal to the snapshot’s size.

Its originating element is the ::page-transition-image-wrapper() pseudo-element with the same tag.

The following user-agent origin styles apply to this element:

html::page-transition-outgoing-image(*) {
  position: absolute;
  inset-block-start: 0;
  inline-size: 100%;
  block-size: auto;

  animation-duration: inherit;
  animation-fill-mode: inherit;
}

Note: The aim of the style is to match the element’s inline size while retaining the aspect ratio. It is also placed at the block start.

In addition to above, styles in the user-agent origin add mix-blend-mode:plus-lighter to this pseudo element if the ancestor ::page-transition-image-wrapper has both ::page-transition-incoming-image and ::page-transition-outgoing-image as descendants.

Note: mix-blend-mode value of plus-lighter ensures that the blending of identical pixels from the outgoing and incoming images results in the same color value as those pixels.

Additional user-agent origin styles added to animate these pseudo-elements are detailed in Animate a page transition.

::page-transition-incoming-image( <pt-tag-selector> )

Identical to ::page-transition-outgoing-image(), except it deals with the incoming element instead.

The precise tree structure, and in particular the order of sibling pseudo-elements, is defined in the Create transition pseudo-elements algorithm.

5. Concepts

5.1. Phases

Phases represent an ordered sequence of states. Since phases are ordered, prose can refer to phases before a particular phase, meaning they appear earlier in the sequence, or after a particular phase, meaning they appear later in the sequence.

The initial phase is the first item in the sequence.

5.2. The page-transition layer stacking layer

This specification introduces a stacking layer to the Elaborate description of Stacking Contexts.

The ::page-transition pseudo-element generates a new stacking context called page-transition layer with the following characteristics:

  1. Its parent stacking context is the root stacking context.

  2. If the page-transition pseudo-element exists, a new stacking context is created for the root and top layer elements. The page-transition layer is a sibling of this stacking context.

  3. The page-transition layer paints after the stacking context for the root and top layer elements.

Note: The intent of the feature is to be able to capture the contents of the page, which includes the top layer elements. In order to accomplish that, the page-transition layer cannot be a part of the captured top layer context, since that results in a circular dependency. Instead, this stacking context is a sibling of other page contents.

Do we need to clarify that the stacking context for the root and top layer elements has filters and effects coming from the root element’s style?

5.3. Captured elements

A captured element is a struct with the following:

outgoing image

an image or null. Initially null.

The type of "image" needs to be linked or defined.

outgoing styles

a set of styles or null. Initially null.

The type of "a set of styles" needs to be linked or defined.

incoming element

an element or null. Initially null.

The type of "element" needs to be linked or defined.

5.4. Additions to Document

A Document additionally has:

active DOM transition

a DOMTransition or null. Initially null.

transition suppressing rendering

a boolean. Initially false.

6. API

6.1. Additions to Document

partial interface Document {
    DOMTransition createTransition(DOMTransitionInit init);
};

dictionary DOMTransitionInit {
    required UpdateDOMCallback updateDOM;
};

callback UpdateDOMCallback = Promise<any> ();

6.1.1. createTransition()

The method steps for createTransition(init) are as follows:
  1. Let transition be a new DOMTransition object in this’s relevant Realm.

  2. Set transition’s DOM update callback to init[updateDOM].

  3. Let document be this’s relevant global object’s associated document.

  4. If document’s active DOM transition is not null, then skip the page transition document’s active DOM transition with an "AbortError" DOMException in this’s relevant Realm.

    Note: This can result in two asynchronous DOM update callbacks running concurrently. One for the document’s current active DOM transition, and another for this transition. As per the [design of this feature](#transitions-as-enhancements), it’s assumed that the developer is using another feature or framework to correctly schedule these DOM changes.

  5. Set document’s active DOM transition to transition.

    Note: The process continues in perform an outgoing capture.

  6. Return transition.

If the default animations for the page transition are acceptable, then kicking off a transition requires nothing more than setting page-transition-tag in the page’s CSS, and a single line of script to start it:
document.createTransition({
    updateDOM() {
        coolFramework.changeTheDOMToPageB();
    }
});

If more precise management is needed, however, transition elements can be managed in script:

async function doTransition() {
    // Specify "outgoing" elements. The tag is used to match against
    // "incoming" elements they should transition to, and to refer to
    // the transitioning pseudo-element.
    document.querySelector('.old-message').style.pageTransitionTag = 'message';

    const transition = document.createTransition({
        async updateDOM() {
            // This callback is invoked by the browser when "outgoing"
            // capture finishes and the DOM can be switched to the new
            // state. No frames are rendered until this callback returns.

            // DOM changes may be asynchronous
            await coolFramework.changeTheDOMToPageB();

            // Tagging elements during the updateDOM() callback marks them as
            // "incoming", to be matched up with the same-tagged "outgoing"
            // elements marked previously and transitioned between.
            document.querySelector('.new-message').style.pageTransitionTag =
                'message';
        },
    });

    // When ready resolves, all pseudo-elements for this transition have
    // been generated.
    // They can now be accessed in script to set up custom animations.
    await transition.ready;

    document.documentElement.animate(keyframes, {
        ...animationOptions,
        pseudoElement: '::page-transition-container(message)',
    });

    // When the finished promise resolves, that means the transition is
    // finished.
    await transition.finished;
}

6.2. The DOMTransition interface

[Exposed=Window]
interface DOMTransition {
    undefined skipTransition();
    readonly attribute Promise<undefined> finished;
    readonly attribute Promise<undefined> ready;
    readonly attribute Promise<undefined> domUpdated;
};
The DOMTransition represents and controls a single same-document transition. That is, it controls a transition where the starting and ending document are the same, possibly with changes to the document’s DOM structure.

A DOMTransition has the following:

tagged elements

a map, whose keys are page transition tags and whose values are captured elements. Initially a new map.

phase

One of the following phases:

  1. "`pending-capture`".

  2. "`dom-update-callback-called`".

  3. "`animating`".

  4. "`done`".

DOM update callback

an UpdateDOMCallback or null. Initially null.

ready promise

a Promise. Initially a new promise in this’s relevant Realm.

DOM updated promise

a Promise. Initially a new promise in this’s relevant Realm.

finished promise

a Promise. Initially a new promise in this’s relevant Realm.

The finished getter steps are to return this’s finished promise.

The ready getter steps are to return this’s ready promise.

The domUpdated getter steps are to return this’s DOM updated promise.

6.2.1. skipTransition()

The method steps for skipTransition() are:
  1. If this's phase is not "`done`", then skip the page transition for this with an "AbortError" DOMException.

7. Algorithms

7.1. Monkey patches to rendering

Run the following steps before [marking paint timing](https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model:mark-paint-timing) in the update the rendering steps:
  1. For each fully active Document in docs, perform pending transition operations for that Document.

Note: These steps will be added to the update the rendering in the HTML spec. As such, the prose style is written to match other steps in that algorithm.

Issue: Define where this sits within the update the rendering steps.
  1. For each Document in docs with a transition suppressing rendering of true:

    Define this behavior. Lifecycle updates can still be triggered via script APIs which query style or layout information but no visual updates are presented to the user. Is this the same behavior as render-blocking?

    How should input be handled when in this state? The last frame presented to the user will not reflect the DOM state as it asynchronously switches to the new version.

    Note: The aim is to prevent unintended DOM updates from being presented to the user after a cached snapshot for the elements has been captured. We wait for one rendering opportunity after prepare to present DOM mutations made by the author before prepare to be presented to the user. This is also the content captured in snapshots.

Note: These steps will be added to the update the rendering in the HTML spec. As such, the prose style is written to match other steps in that algorithm.

7.2. Perform pending transition operations

To perform pending transition operations given a Document document, perform the following steps:
  1. If document’s active DOM transition is not null, then:

    1. If document’s active DOM transition's phase is "`pending-capture`", then perform an outgoing capture with document’s active DOM transition.

    2. Otherwise, if document’s active DOM transition's phase is "`animating`", then update transition DOM for document’s active DOM transition.

7.3. Perform an outgoing capture

To perform an outgoing capture given a DOMTransition transition, perform the following steps:
  1. Let taggedElements be transition’s tagged elements.

  2. Let usedTransitionTags be a new set of strings.

  3. Let document be transition’s relevant global object’s associated document.

  4. For each element of every Element and pseudo-element connected to document, in [paint order](https://drafts.csswg.org/css2/#painting-order):

    The link for "paint order" doesn’t seem right. Is there a more canonical definition?

    1. Let transitionTag be the computed value of page-transition-tag for element.

    2. If transitionTag is none, or element is not rendered, then continue.

    3. If any of the following is true:

      Then skip the page transition for transition with an "InvalidStateError" DOMException in transition’s relevant Realm, and return.

    4. Append transitionTag to usedTransitionTags.

    5. Let capture be a new captured element struct.

    6. Set capture’s outgoing image to the result of capturing the image of element.

    7. Set capture’s outgoing styles to the following:

      transform

      A CSS transform that would place element from the layout viewport origin to its current quad.

      This value is identity for the root element.

      width
      height

      The width and height of element’s border box.

      This value is the bounds of the initial containing block for the root element.

      object-view-box

      An object-view-box value that, when applied to the outgoing image, will cause the view box to coincide with element’s border box in the image.

      writing-mode

      The writing-mode of element.

      direction

      The direction of element.

      This needs proper types.

    8. Set taggedElements[transitionTag] to capture.

  5. Set document’s transition suppressing rendering to true.

  6. Queue a global task on the DOM manipulation task source, given transition’s relevant global object, to execute the following steps:

    Note: A task is queued here because the texture read back in capturing the image may be async, although the render steps in the HTML spec act as if it’s synchronous.

    1. If transition’s phase is "`done`", then abort these steps.

      Note: This happens if transition was skipped before this point.

    2. Call the DOM update callback of transition.

    3. React to transition’s DOM updated promise:

7.4. Skip the page transition

To skip the page transition for DOMTransition transition with reason reason:
  1. Let document be transition’s relevant global object’s associated document.

  2. Assert: document’s active DOM transition is transition.

  3. Assert: transition’s phase is not "`done`".

  4. If transition’s phase is before "`dom-update-callback-called`", then call the DOM update callback of transition.

  5. Set transition suppressing rendering to false.

  6. If transition’s phase is equal to or after "`animating`", then:

    1. Remove all associated page-transition pseudo-elements from document.

      There needs to be a definition/link for "remove".

      There needs to be a definition/link for "associated".

  7. Set transition’s phase to "`done`".

  8. Set document’s active DOM transition to null.

  9. Reject transition’s ready promise with reason.

  10. Reject transition’s finished promise with reason.

7.5. Capture the image

To capture the image given an Element element, perform the following steps. They return an image.
  1. Render the referenced element and its descendants, at the same size that they would be in the document, over an infinite transparent canvas with the following characteristics:

    • The origin of element’s ink overflow rectangle is anchored to canvas origin.

    • If the referenced element has a transform applied to it (or its ancestors), then the transform is ignored.

      Note: This transform is applied to the snapshot using the `transform` property of the associated ::page-transition-container pseudo-element.

    • For each descendant of shadow-including descendant Element and pseudo-element of element, if descendant has a computed value of page-transition-tag that is not none, then skip painting descendant.

      Note: This is necessary since the descendant will generate its own snapshot which will be displayed and animated independently.

      Refactor this so the algorithm takes a set of elements that will be captured. This centralizes the logic for deciding if an element should be included or not.

  2. Let interestRectangle be the result of computing the interest rectangle for element.

    Note: The interestRectangle is the subset of element’s ink overflow rectangle that should be captured. This is required for cases where an element’s ink overflow rectangle needs to be clipped because of hardware constraints. For example, if it exceeds the maximum texture size.

  3. Return the portion of the canvas within interestRectangle as an image. The natural size of the image is equal to the interestRectangle bounds.

7.6. Update transition DOM

To update transition DOM given a DOMTransition transition:
  1. Let document be transition’s relevant global object’s associated document.

  2. For each page-transition pseudo-elements associated with transition, check whether there is an active animation associated with this pseudo-element.

    Define what active animation means here.

  3. If no page-transition pseudo-elements has an active animation:

    There needs to be a definition/link for "active animation".

    1. Set transition’s phase to "`done`".

    2. Remove all associated page-transition pseudo-elements from document.

      There needs to be a definition/link for "remove".

      There needs to be a definition/link for "associated".

    3. Set document’s active DOM transition to null.

    4. Resolve transition’sfinished promise.

    5. Return.

  4. For each tag -> capturedElement of transition’s tagged elements:

    1. If capturedElement has an "incoming element", run capture the image on capturedElement’s "incoming element" and update the displayed image for ::page-transition-incoming-image with the tag tag.

      At the user-agent origin, set incoming’s object-view-box property to a value that when applied to incoming, will cause the view box to coincide with "incoming element"'s border box in the image.

    2. ...

      Also clarify updating the animation based on new bounds/transform to get c0 continuity.

7.7. Compute the interest rectangle

To compute the interest rectangle of an Element el, perform the following steps. They return a rectangle.
  1. If el is the document’s root element, then return a rectangle that is the intersection of the layout viewport, including the size of rendered scrollbars (if any), with el’s ink overflow rectangle.

  2. If el’s ink overflow area does not exceed an implementation-defined maximum size, then return a rectangle that is equal to el’s ink overflow rectangle.

  3. Otherwise:

    Define the algorithm used to clip the snapshot when it exceeds max size.

7.8. Animate a page transition

To animate a page transition given a DOMTransition transition:
  1. Generate a <keyframe> named "page-transition-fade-out" in user-agent origin as follows:

    @keyframes page-transition-fade-out {
          to { opacity: 0; }
    }
    
  2. Generate a <keyframe> named "page-transition-fade-in" in user-agent origin as follows:

    @keyframes page-transition-fade-in {
          from { opacity: 0; }
    }
    
  3. Apply the following styles in user-agent origin:

    html::page-transition-outgoing-image(*) {
        animation-name: page-transition-fade-out;
    }
    
    html::page-transition-incoming-image(*) {
        animation-name: page-transition-fade-in;
    }
    
  4. For each tag -> capturedElement of transition’s tagged elements:

    1. If neither of capturedElement’s outgoing image or incoming element is null:

      1. Let transform be capturedElement’s outgoing styles's transform property.

      2. Let width be capturedElement’s outgoing styles's width property.

      3. Let height be capturedElement’s outgoing styles's height property.

      4. Generate a <keyframe> named "page-transition-container-anim-tag" in user-agent origin as follows:

      @keyframes page-transition-container-anim-|tag| {
          from {
              transform: |transform|;
              width: |width|;
              height: |height|;
          }
      }
      
    2. Apply the following styles in user-agent origin:

      html::page-transition-container(|tag|) {
          animation-name: page-transition-container-anim-|tag|;
      }
      
  5. Set transition’s phase to "`animating`".

How are keyframes scoped to user-agent origin? We could decide scope based on whether `animation-name` in the computed style came from a developer or UA stylesheet.

We should retarget the animation if computed properties for incoming elements change.

7.9. Create transition pseudo-elements

To create transition pseudo-elements for a DOMTransition transition:
  1. Let transitionRoot be the result of creating a new ::page-transition pseudo-element.

  2. For each transitionTagcapturedElement of transition’s tagged elements:

    1. Let container be the result of creating a new ::page-transition-container pseudo-element with the tag transitionTag.

      "tag" should be defined/linked.

    2. Append container to transitionRoot.

      This should be better defined. I’m not sure if pseudo-elements have defined ways to modify their DOM.

    3. Let width, height, transform, writingMode, and direction be null.

    4. If capturedElement’s incoming element is null, then:

      1. Set width to capturedElement’s outgoing styles width property.

      2. Set height to capturedElement’s outgoing styles height property.

      3. Set transform to capturedElement’s outgoing styles transform property.

      4. Set writingMode to capturedElement’s outgoing styles writing-mode property.

      5. Set direction to capturedElement’s outgoing styles direction property.

    5. Otherwise:

      1. Set width to the current width of capturedElement’s incoming element's border box.

      2. Set height to the current height of capturedElement’s incoming element's border box.

      3. Set transform to a transform that maps the capturedElement’s incoming element's border box from document origin to its quad in layout viewport.

      4. Set writingMode to the computed value of writing-mode on capturedElement’s incoming element.

      5. Set direction to the computed value of direction on capturedElement’s incoming element.

    6. At the user-agent origin, set container’s width, height, transform, writing-mode, and direction properties to width, height, transform, writingMode, and direction.

    7. Let imageWrapper be a new ::page-transition-image-wrapper pseudo-element with the tag transitionTag.

    8. Append imageWrapper to container.

    9. If capturedElement’s outgoing image is not null, then:

      1. Let outgoing be a new ::page-transition-outgoing-image replaced element pseudo-element, with the tag transitionTag, displaying capturedElement’s outgoing image.

      2. Append outgoing to imageWrapper.

      3. At the user-agent origin, set outgoing’s object-view-box property to capturedElement’s outgoing styles object-view-box property.

        Which of xywh()/rect()/inset() should we use?

    10. If capturedElement’s incoming element is not null, then:

      1. Let incoming be a new ::page-transition-incoming-image replaced element pseudo-element, with the tag transitionTag, displaying the capture the image of capturedElement’s incoming element.

      2. Append incoming to imageWrapper.

      3. At the user-agent origin, set incoming’s object-view-box property to a value that when applied to incoming, will cause the view box to coincide with incoming element's border box in the image.

      The incoming element and its contents (the flat tree descendants of the element, including both text and elements, or the replaced content of a replaced element), except the page-transition pseudo-elements, are not painted (as if they had visibility: hidden) and do not respond to hit-testing (as if they had pointer-events: none) until incoming exists.

To call the DOM update callback of a DOMTransition transition:
  1. Assert: transition’s phase is before "`dom-update-callback-called`".

  2. Let callbackPromise be the result of invoking transition’s DOM update callback.

  3. Set transition’s phase to "`dom-update-callback-called`".

  4. React to callbackPromise:

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.

Tests

Tests relating to the content of this specification may be documented in “Tests” blocks like this one. Any such block is non-normative.


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 http://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-BOX-4]
Elika Etemad. CSS Box Model Module Level 4. URL: https://andreubotella.com/csswg-auto-build/css-box-4/
[CSS-BREAK-4]
Rossen Atanassov; Elika Etemad. CSS Fragmentation Module Level 4. URL: https://andreubotella.com/csswg-auto-build/css-break-4/
[CSS-CASCADE-5]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 5. URL: https://andreubotella.com/csswg-auto-build/css-cascade-5/
[CSS-CONTAIN-2]
Tab Atkins Jr.; Florian Rivoal; Vladimir Levin. CSS Containment Module Level 2. URL: https://andreubotella.com/csswg-auto-build/css-contain-2/
[CSS-DISPLAY-3]
Tab Atkins Jr.; Elika Etemad. CSS Display Module Level 3. URL: https://andreubotella.com/csswg-auto-build/css-display/
[CSS-IMAGES-3]
Tab Atkins Jr.; Elika Etemad; Lea Verou. CSS Images Module Level 3. URL: https://andreubotella.com/csswg-auto-build/css-images-3/
[CSS-IMAGES-4]
Tab Atkins Jr.; Elika Etemad; Lea Verou. CSS Image Values and Replaced Content Module Level 4. URL: https://andreubotella.com/csswg-auto-build/css-images-4/
[CSS-OVERFLOW-3]
David Baron; Elika Etemad; Florian Rivoal. CSS Overflow Module Level 3. URL: https://andreubotella.com/csswg-auto-build/css-overflow-3/
[CSS-SHAPES-1]
Vincent Hardy; Rossen Atanassov; Alan Stearns. CSS Shapes Module Level 1. URL: https://andreubotella.com/csswg-auto-build/css-shapes/
[CSS-SIZING-3]
Tab Atkins Jr.; Elika Etemad. CSS Box Sizing Module Level 3. URL: https://andreubotella.com/csswg-auto-build/css-sizing-3/
[CSS-TRANSFORMS-1]
Simon Fraser; et al. CSS Transforms Module Level 1. URL: https://andreubotella.com/csswg-auto-build/css-transforms/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 4. URL: https://andreubotella.com/csswg-auto-build/css-values-4/
[CSS-WRITING-MODES-3]
Elika Etemad; Koji Ishii. CSS Writing Modes Level 3. URL: https://andreubotella.com/csswg-auto-build/css-writing-modes-3/
[CSS-WRITING-MODES-4]
Elika Etemad; Koji Ishii. CSS Writing Modes Level 4. URL: https://andreubotella.com/csswg-auto-build/css-writing-modes-4/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[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
[SELECTORS-3]
Tantek Çelik; et al. Selectors Level 3. URL: https://andreubotella.com/csswg-auto-build/selectors-3/
[SELECTORS-4]
Elika Etemad; Tab Atkins Jr.. Selectors Level 4. URL: https://andreubotella.com/csswg-auto-build/selectors/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

Property Index

Name Value Initial Applies to Inh. %ages Anim­ation type Canonical order Com­puted value
page-transition-tag none | <custom-ident> none all elements no n/a discrete per grammar as specified

IDL Index

partial interface Document {
    DOMTransition createTransition(DOMTransitionInit init);
};

dictionary DOMTransitionInit {
    required UpdateDOMCallback updateDOM;
};

callback UpdateDOMCallback = Promise<any> ();

[Exposed=Window]
interface DOMTransition {
    undefined skipTransition();
    readonly attribute Promise<undefined> finished;
    readonly attribute Promise<undefined> ready;
    readonly attribute Promise<undefined> domUpdated;
};

Issues Index

The selector for this and subsequently defined pseudo-elements is likely to change to indicate position in the pseudo-tree hierarchy.
Isolation is only necessary to get the right cross-fade between incoming and outgoing image pixels. Would it be simpler to always add it and try to optimize in the implementation?
Do we need to clarify that the stacking context for the root and top layer elements has filters and effects coming from the root element’s style?
The type of "image" needs to be linked or defined.
The type of "a set of styles" needs to be linked or defined.
The type of "element" needs to be linked or defined.
Define this behavior. Lifecycle updates can still be triggered via script APIs which query style or layout information but no visual updates are presented to the user. Is this the same behavior as render-blocking?
How should input be handled when in this state? The last frame presented to the user will not reflect the DOM state as it asynchronously switches to the new version.
The link for "paint order" doesn’t seem right. Is there a more canonical definition?
This needs proper types.
The link for "paint order" doesn’t seem right. Is there a more canonical definition?
There needs to be a definition/link for "remove".
There needs to be a definition/link for "associated".
Refactor this so the algorithm takes a set of elements that will be captured. This centralizes the logic for deciding if an element should be included or not.
Define what active animation means here.
There needs to be a definition/link for "active animation".
There needs to be a definition/link for "remove".
There needs to be a definition/link for "associated".
Also clarify updating the animation based on new bounds/transform to get c0 continuity.
Define the algorithm used to clip the snapshot when it exceeds max size.
How are keyframes scoped to user-agent origin? We could decide scope based on whether `animation-name` in the computed style came from a developer or UA stylesheet.
We should retarget the animation if computed properties for incoming elements change.
"tag" should be defined/linked.
This should be better defined. I’m not sure if pseudo-elements have defined ways to modify their DOM.
Which of xywh()/rect()/inset() should we use?