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:
-
The user navigates, by clicking a link, submitting a form, traversing history using the browser UI, etc.
-
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. -
An event named
reveal
is fired on the newDocument
, with aviewTransition
property, which is aViewTransition
object. ThisViewTransition
's
is already resolved, and its captured elements are populated from the oldupdateCallbackDone
Document
. -
Right before the new
Document
has the first rendering opportunity, its state is captured as the "new" state. -
From this point forward, the transition continues as if it was a same-document transition, as per activate view transition.
1.2. Examples
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.
-
Opt-in to navigation-triggered view-transitions in both pages.
-
Pass the click location to the new document, e.g. via
sessionStorage
. -
Intercept the
ViewTransition
object in the new document, using thePageRevealEvent
.
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) return ; 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 document. documentElement. animate( { 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)' } ); })
2. Pseudo-classes
2.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:
:active-view-transition(<vt-type-selector>) <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:
-
The <vt-type-selector> is *
-
viewTransition’s active types contains at least one of the <custom-ident> values of the <vt-type-selector>.
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 *'':
document. startViewTransition( updateTheDOMSomehow); // 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) {}
3. CSS rules
3.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 it is reveal.
3.2. @view-transition rule grammar
The @view-transition rule has the following syntax:
@view-transition { <declaration-rule-list> }
Note: as per default behavior, the @view-transition rule can be nested inside a conditional group rule such as @media or @supports.
3.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).
- none
-
There will be no transition.
- auto
-
The transition will be enabled if the navigation is same-origin, without cross-origin redirects, and is not a
reload
.
3.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
.
4. API
4.1. The PageRevealEvent
Note: this should go in the HTML spec. See Issue 9315.
[Exposed =Window ]interface PageRevealEvent :Event {readonly attribute ViewTransition ?; };
viewTransition
A PageRevealEvent
has a null-or-ViewTransition
view transition, initially null.
Note: this event is fired when the document is reveal.
The viewTransition
getter steps are to return this's view transition.
4.2. Additions to Document
dictionary {
StartViewTransitionOptions UpdateCallback ?=
update null ;sequence <DOMString >?=
type null ; };partial interface Document {ViewTransition startViewTransition (optional (UpdateCallback or StartViewTransitionOptions )= {}); };
callbackOptions
4.2.1. startViewTransition(callbackOptions)
Method Steps
startViewTransition(callbackOptions)
are as follows:
-
If callbackOptions is not provided, then run the method steps for
startViewTransition()
and return the result. -
If callbackOptions is an
UpdateCallback
, then run the method steps forstartViewTransition(updateCallback)
given callbackOptions and return the result. -
Let viewTransition be the result of running method steps for
startViewTransition(updateCallback)
given callbackOptions’supdate
. -
Set viewTransition’s active types to callbackOptions’s
type
. -
Return viewTransition.
4.3. Extensions to the CSSRule
interface
The CSSRule
interface is extended as follows:
partial interface CSSRule {const unsigned short = 15; };
VIEW_TRANSITION_RULE
4.4. The CSSViewTransitionRule
interface
The CSSViewTransitionRule
represents a @view-transition rule.
enum {
ViewTransitionNavigation ,
"auto" }; [
"none" Exposed =Window ]interface :
CSSViewTransitionRule CSSRule {attribute ViewTransitionNavigation ;
navigation attribute DOMTokenList ; };
type
5. Algorithms
5.1. Data Structures
5.1.1. Additions to Document
A Document
additionally has:
- pagereveal fired
-
a boolean, initially false.
- is inbound cross-document transition
-
a boolean, initially false.
- active types
-
Null or a list of strings, initially null.
-
For each fully active
Document
doc in docs, run the reveal steps for doc. -
Set document’s pagereveal fired to false.
-
If document’s page pagereveal fired is true, then return.
-
Let transition be the result of resolving cross-document view-transition for document.
-
Fire an event named
pagereveal
at document’s relevant global object, usingPageRevealEvent
, with view transition initialized to transition. -
If transition is not null, then activate transition.
-
Set document’s page pagereveal fired to true.
-
Let matchingRule be the last @view-transition rule in document.
-
If matchingRule is not found, then return "
skip transition
". -
If matchingRule’s navigation descriptor’s computed value is none, then return "
skip transition
". -
Assert: matchingRule’s navigation descriptor’s computed value is auto.
-
Let typesDescriptor be matchingRule’s type descriptor.
-
If typesDescriptor’s computed value is none, then return an empty list.
-
Return a list of strings corresponding to typesDescriptor’s computed value.
-
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.
-
If navigationType is
reload
, then return. -
If oldDocument’s origin is not same origin as newDocument’s origin then call onReady and return.
-
If newDocument was created via cross-origin redirects is true and newDocument’s latest entry is null, then call onReady and return.
Note: A document with a non-null latest entry is being reactivated, in which case we don’t need to check for cross-origin redirects.
-
Resolve @view-transition rule for oldDocument and let resolvedRule be the result.
-
If resolvedRule is "
skip transition
", then call onReady and return.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 it is reveal.
-
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.
-
Set document’s auto-skip view transitions to true.
Note: this means that calling
startViewTransition()
while capturing the old document for a cross-document view-transition would run the callback but skip the animation. -
Let outboundTransition be a new
ViewTransition
object in oldDocument’s relevant Realm, whose active types is resolvedRule, and whose process old state captured is set to the following steps:should we check for the opt-in again, in case there was a CSSOM change in a requestAnimationFrame callback?
-
If outboundTransition’s phase is "
done
", then call onReady and return. -
Assert: outboundTransition’s phase is "
pending-capture
". -
Clear view transition outboundTransition.
Note: The ViewTransition object on the old Document should be destroyed after its state has been copied to the new Document below. We explicitly clear it here since the old Document may be cached by the UA.
-
Set document’s auto-skip view transitions to false.
-
Queue a global task on the DOM manipulation task source given newDocument’s relevant global object, to perform the following steps:
-
Let inboundTransition be a new
ViewTransition
in newDocument’s relevant Realm, whose named elements is outboundTransition’s named elements, initial snapshot containing block size is outboundTransition’s initial snapshot containing block size, and whose is inbound cross-document transition is true. -
Let newDocument’s active view transition be inboundTransition.
-
Call the update callback for inboundTransition.
-
Call onReady.
-
At any given time, the UA may decide to skip inboundTransition, 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
.
-
Note: outboundTransition is not exposed to JavaScript, it is used only for capturing the state of the old document.
-
-
Set oldDocument’s active view transition to outboundTransition.
Note: The process continues in setup view transition, via perform pending transition operations.
-
The user agent should display the currently displayed frame until either:
-
newDocument is revealed
-
its active view transition's phase is "
done
".
Note: this is to ensure that there are no unintended flashes between displaying the old and new state, to keep the transition smooth.
-
-
Let transition be document’s active view transition.
-
If transition is null or transition’s is inbound cross-document transition is false, then return null.
Note: transition’s is inbound cross-document transition would be false if a same-document transition was started before the page was reveal.
-
Resolve @view-transition rule for document and let resolvedRule be the result.
-
If resolvedRule is "
skip transition
", then skip transition and return null. -
Set transition’s active types to resolvedRule.
-
Return transition.
-
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. -
A same-origin navigation might still occur via a cross-origin redirect, e.g.
https://example.com
links tohttps://auth-provider.com/
which redirects back tohttps://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 theDocument
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.
-
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.
5.1.2. Additions to ViewTransition
A ViewTransition
additionally has:
should a cross-document transition take precedent? See #9512
5.2. Monkey patches to HTML
If changingNavigationContinuation update-only is false, then setup cross-document view-transition given oldDocument, newDocument, navigationType, 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.
Document
document:
5.3. Setting up and activating the cross-document view transition
5.3.1. Resolving the @view-transition' rule
Document
document:
5.3.2. Setting up the view-transition in the old Document
Document
oldDocument,
a Document
newDocument, a NavigationType
navigationType, and onReady, which is an algorithm accepting nothing:
5.3.3. Accessing the view-transition in the new Document
Document
document:
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:
See Issue #8684 and WICG/view-transitions#200 for detailed discussion.