CSS View Transitions Module Level 2

Editor’s Draft,

More details about this document
This version:
Issue Tracking:
CSSWG Issues Repository
Inline In Spec
Noam Rosenthal (Google)
Khushal Sagar (Google)
Vladimir Levin (Google)
Tab Atkins-Bittner (Google)
Suggest an Edit for this Spec:
GitHub Editor


This module defines how the View Transition API works with cross-document navigations.

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-view-transitions-2” in the title, like this: “[css-view-transitions-2] …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 03 November 2023 W3C Process Document.

1. Introduction

This section is non-normative.

View Transitions, as specified in [css-view-transitions-1], is a feature that allows developers to create animated transitions between visual states of the document.

Level 2 extends that specification, by adding the necessary API and lifecycle to enable transitions across a same-origin cross-document navigation.

1.1. Lifecycle

This section is non-normative.

A successful cross-document view transition goes through the following phases:

  1. The user navigates, by clicking a link, submitting a form, traversing history using the browser UI, etc.

  2. Once it’s time to unload the old document, if the navigation is same origin and the old Document has opted in to cross-document view-transitions, the old state is captured.

  3. An event named pagereveal is fired on the new Document, with a viewTransition property, which is a ViewTransition object. This ViewTransition's updateCallbackDone is already resolved, and its captured elements are populated from the old Document.

  4. Right before the new Document has the first rendering opportunity, its state is captured as the "new" state.

  5. From this point forward, the transition continues as if it was a same-document transition, as per activate view transition.

1.2. Examples

1.2.1. Cross-document view-transitions

To generate the same cross-fade as in the first example CSS View Transitions 1 § 1.6 Examples, but across documents, we don’t need JavaScript.

Instead, we opt in to triggering view-transitions on navigation in both page 1 and page 2:

// in both documents:
@view-transition {
  navigation: auto;

A link from page 1 to or from page 2 would generate a crossfade transition for example 1. To achieve the effect examples 2, 3 & 4, simply put the CSS for the pseudo-elements in both documents.

To achieve the effect in example 5, we have to do several things:

In both pages:

@view-transition {
  navigation: auto;

In the old page:

addEventListener('click', event => {
  sessionStorage.setItem("lastClickX", event.clientX);
  sessionStorage.setItem("lastClickY", event.clientY);

In the new page:

// This would run both on initial load and on reactivation from BFCache.
addEventListener("pagereveal", async event => {
  if (!event.viewTransition)

  const x = sessionStorage.getItem("lastClickX") ?? innerWidth / 2;
  const y = sessionStorage.getItem("lastClickY") ?? innerHeight / 2;

  const endRadius = Math.hypot(
    Math.max(x, innerWidth - x),
    Math.max(y, innerHeight - y)

  await event.viewTransition.ready;

  // Animate the new document's view
      clipPath: [
        `circle(0 at ${x}px ${y}px)`,
        `circle(${endRadius}px at ${x}px ${y}px)`,
      duration: 500,
      easing: 'ease-in',
      pseudoElement: '::view-transition-new(root)'

1.2.2. view-transition-class

view-transition-class provides a way to use the same style for multiple view transition pseudo elements without having to replicate the corresponding pseudo-elements.

This example creates a transition with each box participating under its own name, while applying a 1-second duration to the animation of all the boxes:
<div class="box" id="red-box"></div>
<div class="box" id="green-box"></div>
<div class="box" id="yellow-box"></div>
div.box {
  view-transition-class: any-box;
  width: 100px;
  height: 100px;
#red-box {
  view-transition-name: red-box;
  background: red;
#green-box {
  view-transition-name: green-box;
  background: green;
#yellow-box {
  view-transition-name: yellow-box;
  background: yellow;

/* The following style would apply to all the boxes, thanks to 'view-transition-class' */
::view-transition-group(*.any-box) {
  animation-duration: 1s;

2. CSS Properties

2.1. Applying the same style to multiple participating elements: the view-transition-class property

Name: view-transition-class
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 view-transition-class can be used to apply the same style rule to multiple named view transition pseudo-elements which may have a different view-transition-name. While view-transition-name is used to match between the element in the old state with its corresponding element in the new state, view-transition-class is used only to apply styles using the view-transition pseudo-elements (::view-transition-group(), ::view-transition-image-pair(), ::view-transition-old(), ::view-transition-new()).

Note that view-transition-class by itself doesn’t mark an element for capturing, it is only used as an additional way to style an element that already has a view-transition-name.


No class would apply to the named view transition pseudo-elements generated for this element.


All of the specified <custom-ident> values (apart from none) are applied when used in named view transition pseudo-element selectors. none is an invalid <custom-ident> for view-transition-class, even when combined with other <custom-ident>s.

Note: If the same view-transition-name is specified for an element both in the old and new states of the transition, only the view-transition-class values from the new state apply. This also applies for cross-document view-transitions: classes from the old document would only apply if their corresponding view-transition-name was not specified in the new document.

3. Pseudo-classes

3.1. Active View Transition Pseudo-class :active-view-transition()'

The :active-view-transition(<vt-type-selector>) pseudo-class applies to the root element of the document, if it has a matching active view transition. It has the following syntax definition:

<vt-type-selector> = '*' | <custom-ident>#

The specificity of an :active-view-transition() is one pseudo-class selector if it has value is *, and two if it has any other value.

An :active-view-transition() pseudo-class matches the document element when it has an non-null active view transition viewTransition, for which any of the following are true:

For example, the developer might start a transition in the following manner:
document.startViewTransition({update: updateTheDOMSomehow, types: ["slide-in", "reverse"]});

This will activate any of the following ':active-view-transition()'' selectors:

:root:active-view-transition(slide-in) {}
:root:active-view-transition(reverse) {}
:root:active-view-transition(slide-in, reverse) {}
:root:active-view-transition(slide-in, something-else) {}
:root:active-view-transition(*) {}

While starting a transition without selecting transition types, would only activate ':active-view-transition() with *'':

// or
document.startViewTransition({update: updateTheDOMSomehow});
/* This would be active */
:root { }
:root:active-view-transition(*) {}

/* This would not be active */
:root:active-view-transition(slide-in) {}
:root:active-view-transition(any-type-at-all-except-star) {}

4. Additions to named view-transition pseudo-elements

The named view transition pseudo-elements (view-transition-group(), view-transition-image-pair(), view-transition-old(), view-transition-new()) are extended to support the following syntax:


where <pt-name-selector> works as previously defined, and <pt-class-selector> has the following syntax definition:

<pt-class-selector> = ['.' <custom-ident>]*

A named view transition pseudo-element selector which has one or more <custom-ident> values in its <pt-class-selector> would only match an element if the class list value in named elements for the pseudo-element’s view-transition-name contains all of those values.

The specificity of a named view transition pseudo-element selector with either:

is equivalent to a type selector.

The specificity of a named view transition pseudo-element selector with a * argument and with an empty <pt-class-selector> is zero.

5. CSS rules

5.1. The @view-transition rule

The @view-transition rule is used by a document to indicate that cross-document navigations should setup and activate a ViewTransition. To take effect, it must be present in the old document when unloading, and in the new document when the pagereveal is fired.

5.2. @view-transition rule grammar

The @view-transition rule has the following syntax:

@view-transition {

Note: as per default behavior, the @view-transition rule can be nested inside a conditional group rule such as @media or @supports.

5.3. The navigation descriptor

Name: navigation
For: @view-transition
Value: auto | none
Initial: none

The 'navigation' descriptor opts in to automatically starting a view transition when performing a navigation of a certain type. It needs to be enabled both in the old document (when unloading) and in the new document (when reveal).


There will be no transition.


The transition will be enabled if the navigation is same-origin, without cross-origin redirects, and whoes NavigationType is

Note: Navigations excluded from auto are for example, navigating via the URL address bar or clicking a bookmark, as well as any form of user or script initiated reload.

5.4. The type descriptor

Name: type
For: @view-transition
Value: none | <custom-ident>*
Initial: none

The 'type' descriptor sets the active types for the transition when capturing and performing the transition, equivalent to calling startViewTransition(callbackOptions) with that type.

6. API

6.1. Additions to Document

dictionary StartViewTransitionOptions {
  UpdateCallback? update = null;
  sequence<DOMString>? type = null;

partial interface Document {

  ViewTransition startViewTransition(optional (UpdateCallback or StartViewTransitionOptions) callbackOptions = {});

6.1.1. startViewTransition(callbackOptions) Method Steps

The method steps for startViewTransition(callbackOptions) are as follows:
  1. Let updateCallback be null.

  2. If callbackOptions is an an UpdateCallback, set updateCallback to callbackOptions.

  3. Otherwise, if callbackOptions is a StartViewTransitionOptions, then set updateCallback to callbackOptions’s update.

  4. If this’s active view transition is not null and its outbound post-capture steps is not null, then:

    1. Let preSkippedTransition be a new ViewTransition in this’s relevant realm whose update callback is updateCallback.

    2. Skip preSkippedTransition with an "InvalidStateError" DOMException.

    3. Return preSkippedTransition.

  5. Let viewTransition be the result of running the method steps for startViewTransition(updateCallback) given updateCallback.

  6. If callbackOptions is a StartViewTransitionOptions, then set viewTransition’s active types to callbackOptions’s type.

  7. Return viewTransition.

6.2. Extensions to the CSSRule interface

The CSSRule interface is extended as follows:

partial interface CSSRule {
  const unsigned short VIEW_TRANSITION_RULE = 15;

The CSSViewTransitionRule represents a @view-transition rule.

enum ViewTransitionNavigation { "auto", "none" };
interface CSSViewTransitionRule : CSSRule {
  attribute ViewTransitionNavigation navigation;
  attribute DOMTokenList typeList;

7. Algorithms

7.1. Data Structures

7.1.1. Additions to Document

A Document additionaly has:
inbound view transition params

a view transition params, or null. Initially null.

7.1.2. The View transition params struct

A view transition params is a struct whose purpose is to serialize view transition information across documents. It has the following items:

named elements

a map, whose keys are strings and whose values are captured elements.

initial snapshot containing block size

a tuple of two numbers (width and height).

7.1.3. Additions to ViewTransition

A ViewTransition additionally has:

outbound post-capture steps

Null or a set of steps, initially null.

active types

Null or a list of strings, initially null.

7.1.4. Additions to captured element struct

The captured element struct should contain these fields, in addition to the existing ones:

class list

a list of strings, initially empty.

7.2. Algorithm to capture view-transition-class:

When capturing the old or new state for an element, perform the following steps given a captured element capture and an element element:
  1. Set capture’s class list to the computed value of element’s view-transition-class.

Note: This is written in a monkey-patch manner, and will be merged into the algorithm once the L1 spec graduates.

7.3. Additions to skip steps:

Append the following steps to skip the view transition given a ViewTransition transition:
  1. If transition’s outbound post-capture steps is not null, then run transition’s outbound post-capture steps with null.

Note: This is written in a monkey-patch manner, and will be merged into the algorithm once the L1 spec graduates.

7.4. Addition to pending transition operations

Prepend this to the Perform pending transition operations algorithm given a Document document:

  1. If document’s active view transition is not null and its outbound post-capture steps is not null, then:

    1. Assert: document’s active view transition's phase is "pending-capture".

    2. Let viewTransitionParams be null;

    3. Set document’s rendering suppression for view transitions to true.

      though capture the old state appears here as a synchronous step, it is in fact an asynchronous step as rendering an element into an image cannot be done synchronously. This should be more explicit in the L1 spec.

    4. Capture the old state for transition.

    5. Set document’s rendering suppression for view transitions to false.

    6. If this succeeded, then set viewTransitionParams to a new view transition params whose named elements is a clone of transition’s named elements, and whose initial snapshot containing block size is transition’s initial snapshot containing block size.

    7. Call transition’s outbound post-capture steps given viewTransitionParams.

7.5. Monkey patches to HTML

Prepend these steps at the beginning of the task queued on navigable’s active window when applying the history step (14.11.1, here):

This monkey-patch step assumes a boolean changingNavigationContinuation, a navigable navigable, a Document oldDocument, a Document newDocument, a NavigationType navigationType, and a user navigation involvement userInvolvementForNavigateEvents.

  1. Let isBrowserUINavigation be true if userInvolvementForNavigateEvents is "browser UI", otherwise false.

  2. If changingNavigationContinuation update-only is false, then setup cross-document view-transition given oldDocument, newDocument, navigationType, isBrowserUINavigation, and the remaining steps and return from these steps.

Note: This would wait until a transition is captured or skipped before proceeding to unloading the old document and activating the new one.

7.6. Setting up and activating the cross-document view transition

7.6.1. Resolving the @view-transition' rule

To get the resolve @view-transition rule for a Document document:
  1. If document’s visibility state is "hidden", then return "skip transition".

  2. Let matchingRule be the last @view-transition rule in document.

  3. If matchingRule is not found, then return "skip transition".

  4. If matchingRule’s navigation descriptor’s computed value is none, then return "skip transition".

  5. Assert: matchingRule’s navigation descriptor’s computed value is auto.

  6. Let typesDescriptor be matchingRule’s type descriptor.

  7. If typesDescriptor’s computed value is none, then return a list « ».

  8. Return a list of strings corresponding to typesDescriptor’s computed value.

7.6.2. Setting up the view-transition in the old Document

To check if a navigation can trigger a cross-document view-transition? given an origin oldOrigin, an origin newOrigin, a boolean navigationHasCrossOriginRedirects, a NavigationType navigationType, and a boolean isBrowserUINavigation:

Note: this is called during navigation, potentially in parallel, for documents that have opted-in to view-transitions using the @view-transition rule.

  1. If the user agent decides to display an implementation-defined navigation experience, e.g. a gesture-based transition for a back navigation, the user agent may ignore the author-defined view transition. If that is the case, return false.

  2. If navigationType is reload, then return false.

  3. If isBrowserUINavigation is true, and navigationType is push or replace, then return false.

  4. If oldOrigin is not same origin as newOrigin then return false.

  5. If navigationHasCrossOriginRedirects is true, then return false.

  6. Return true.

To setup cross-document view-transition given a Document oldDocument, a Document newDocument, and proceedWithNavigation, which is an algorithm accepting nothing:
  1. Assert: These steps are running as part of a task queued on oldDocument.

  2. If oldDocument’s has been revealed is false, then return null.

  3. Let resolvedRule be the result of resolving the @view-transition rule for oldDocument.

  4. If resolvedRule is "skip transition", then return null.

    Note: We don’t know yet if newDocument has opted in, as it might not be parsed yet. We check the opt-in for newDocument when we fire the pagereveal event.

  5. If oldDocument’s active view transition is not null, then skip oldDocument’s active view transition with an "AbortError" DOMException in oldDocument’s relevant Realm.

    Note: this means that any running transition would be skipped when the document is ready to unload.

  6. Let outboundTransition be a new ViewTransition object in oldDocument’s relevant Realm, whose active types is resolvedRule.

    Note: the ViewTransition is skipped once the old document is hidden.

  7. Set outboundTransition’s outbound post-capture steps to the following steps given a view transition params-or-null params:

    1. Set newDocument’s inbound view transition params to params.

      Note: The inbound transition is activated after the dispatch of pagereveal to ensure mutations made in this event apply to the captured new state.

    2. Call proceedWithNavigation.

  8. Set oldDocument’s active view transition to outboundTransition.

    Note: The process continues in perform pending transition operations.

  9. The user agent should display the currently displayed frame until either:

    Note: this is to ensure that there are no unintended flashes between displaying the old and new state, to keep the transition smooth.

  10. Return outboundTransition.

7.6.3. Accessing the view-transition in the new Document

To resolve cross-document view-transition for Document document:
  1. Assert: document is fully active.

  2. Let inboundViewTransitionParams be document’s inbound view transition params.

  3. If inboundViewTransitionParams is null, then return null.

  4. Set document’s inbound view transition params to null.

  5. If document’s active view transition is not null, then return null.

    Note: this means that starting a same-document transition before revealing the document would cancel a pending cross-document transition.

  6. Resolve @view-transition rule for document and let resolvedRule be the result.

  7. If resolvedRule is "skip transition", then return null.

  8. Let transition be a new ViewTransition in document’s relevant Realm, whose named elements is inboundViewTransitionParams’s named elements, and initial snapshot containing block size is inboundViewTransitionParams’s initial snapshot containing block size.

  9. Set document’s active view transition to transition.

  10. Resolve transition’s update callback done promise with undefined.

  11. Set transition’s phase to "update-callback-called".

  12. Set transition’s active types to resolvedRule.

  13. At any given time, the UA may decide to skip the inbound transition, e.g. after an implementation-defined timeout. To do so, the UA should queue a global task on the DOM manipulation task source given document’s relevant global object to perform the following step: If transition’s phase is not "done", then skip the view transition transition with a "TimeoutError" DOMException.

  14. Return transition.

Privacy Considerations

This specification introduces no new privacy considerations.

Security Considerations

To prevent cross-origin issues, at this point cross-document view transitions can only be enabled for same-origin navigations. As discussed in WICG/view-transitions#200, this still presents two potential threats:

  1. The cross-origin isolated capability in both documents might be different. This can cause a situation where a Document that is cross-origin isolated can read image data from a document that is not cross-origin isolated. This is already mitigated in [[css-view-transitions-1#sec], as the same restriction applies for captured cross-origin iframes.

  2. A same-origin navigation might still occur via a cross-origin redirect, e.g. https://example.com links to https://auth-provider.com/ which redirects back to https://example.com/loggedin.

    This can cause a (minor) situation where the cross-origin party would redirect the user to an unexpected first-party URL, causing an unexpected transition and obfuscating that fact that there was a redirect. To mitigate this, currently view transitions are disabled for navigations if the Document was created via cross-origin redirects. Note that this check doesn’t apply when the Document is being reactivated, as in that case the cross-origin redirect has already taken place.

    Note: this only applies to server-side redirects. A client-side redirect, e.g. using [^meta/http-equiv/refresh^], is equivalent to a new navigation.

  3. This feature exposes more information to CSS, as so far CSS was not aware of anything navigation-related. This can raise concerns around safety 3rd-party CSS. However, as a general rule, 3rd-party stylesheets should come from trusted sources to begin with, as CSS can learn about the document or change it in many ways.

See Issue #8684 and WICG/view-transitions#200 for detailed discussion.


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 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.
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.


Terms defined by this specification

Terms defined by reference


Normative References

Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 5. URL: https://drafts.csswg.org/css-cascade-5/
Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. URL: https://drafts.csswg.org/css-syntax/
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 4. URL: https://drafts.csswg.org/css-values-4/
Tab Atkins Jr.; Jake Archibald; Khushal Sagar. CSS View Transitions Module Level 1. URL: https://drafts.csswg.org/css-view-transitions-1/
Bert Bos. Cascading Style Sheets Level 2 Revision 2 (CSS 2.2) Specification. URL: https://drafts.csswg.org/css2/
Daniel Glazman; Emilio Cobos Álvarez. CSS Object Model (CSSOM). URL: https://drafts.csswg.org/cssom/
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
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
Tantek Çelik; et al. Selectors Level 3. URL: https://drafts.csswg.org/selectors-3/
Elika Etemad; Tab Atkins Jr.. Selectors Level 4. URL: https://drafts.csswg.org/selectors/
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

Informative References

David Baron; Elika Etemad; Chris Lilley. CSS Conditional Rules Module Level 3. URL: https://drafts.csswg.org/css-conditional-3/

Property Index

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

@view-transition Descriptors

Name Value Initial
navigation auto | none none
type none | <custom-ident>* none

IDL Index

dictionary StartViewTransitionOptions {
  UpdateCallback? update = null;
  sequence<DOMString>? type = null;

partial interface Document {

  ViewTransition startViewTransition(optional (UpdateCallback or StartViewTransitionOptions) callbackOptions = {});

partial interface CSSRule {
  const unsigned short VIEW_TRANSITION_RULE = 15;

enum ViewTransitionNavigation { "auto", "none" };
interface CSSViewTransitionRule : CSSRule {
  attribute ViewTransitionNavigation navigation;
  attribute DOMTokenList typeList;

Issues Index

though capture the old state appears here as a synchronous step, it is in fact an asynchronous step as rendering an element into an image cannot be done synchronously. This should be more explicit in the L1 spec.