view-transition-name
Limited availability
This feature is not Baseline because it does not work in some of the most widely-used browsers.
The view-transition-name
CSS property specifies the view transition snapshot that selected elements will participate in. This enables you to animate those elements separately from the rest of the page, which uses the default cross-fade animation during a view transition. You can then define custom animation styles for these elements.
Syntax
/* <custom-ident> value examples */
view-transition-name: header;
view-transition-name: figure-caption;
/* Keyword value */
view-transition-name: none;
view-transition-name: match-element;
/* Global values */
view-transition-name: inherit;
view-transition-name: initial;
view-transition-name: revert;
view-transition-name: revert-layer;
view-transition-name: unset;
Values
<custom-ident>
-
An identifying name that causes the selected element to participate in a separate snapshot from the root snapshot. The
<custom-ident>
cannot beauto
,match-element
,none
, or a CSS-wide keyword value. match-element
-
The browser automatically assigns a unique name to the selected element. This name is used to snapshot the element separately from all other elements on the page. (This name is internal and cannot be read from the DOM.)
none
-
The selected element will not participate in a separate snapshot, unless it has a parent element with a
view-transition-name
set, in which case it will be snapshotted as part of that element.
Description
By default, when a view transition is applied to a web app, all changes to the UI that occur during that transition are snapshotted and animated together. This is the default — or root
— snapshot (see The view transition pseudo-element tree). By default, this animation is a smooth cross-fade, which can be seen in action in the View Transitions SPA demo.
If you want certain elements to be animated differently from the root
snapshot during the view transition, you can do so by giving them a different view-transition-name
, for example:
figcaption {
view-transition-name: figure-caption;
}
You can then specify which animations you want for the before and after snapshots using the relevant view transition pseudo-elements — ::view-transition-old()
and ::view-transition-new()
. For example:
::view-transition-old(figure-caption) {
animation: 0.25s linear both shrink-x;
}
::view-transition-new(figure-caption) {
animation: 0.25s 0.25s linear both grow-x;
}
If you don't want an element to be snapshotted separately, you can specify a view-transition-name
value of none
:
.dont-animate-me {
view-transition-name: none;
}
The view-transition-name
<custom-ident>
must be unique for each rendered element taking part in the view transition. If two rendered elements have the same view-transition-name
at the same time, the ViewTransition.ready
Promise
will reject and the transition will be skipped.
Specifying view-transition-name
values automatically
Sometimes you will want to animate multiple UI elements separately in a view transition. This is often the case when you have a list of elements on a page and want to rearrange them in some way:
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<!-- ... -->
<li>Item 99</li>
</ul>
Giving each one a unique name can be inconvenient, especially as the number of elements gets larger:
li:nth-child(1) {
view-transition-name: item1;
}
li:nth-child(2) {
view-transition-name: item2;
}
li:nth-child(3) {
view-transition-name: item3;
}
li:nth-child(4) {
view-transition-name: item4;
}
/* ... */
li:nth-child(99) {
view-transition-name: item99;
}
To get around this problem, you can use the match-element
value, which causes the browser to give each selected element a unique internal view-transition-name
:
li {
view-transition-name: match-element;
}
Because match-element
assigns automatic view-transition-name
values based on element identity, it can only be used for same-document view transitions. The auto-generated internal identifiers are not transferrable across different elements or documents.
Formal definition
Initial value | none |
---|---|
Applies to | all elements |
Inherited | no |
Computed value | as specified |
Animation type | discrete |
Formal syntax
view-transition-name =
none |
<custom-ident>
Examples
Basic view-transition-name
usage
This example comes from the View Transitions SPA demo, which is a basic image gallery. The Basic SPA view transition provides a more detailed explanation of how this demo works.
Most of the UI changes are animated using the root
transition snapshot. However, the <figcaption>
is given a view-transition-name
of figure-caption
to allow it to be animated differently from the rest of the page:
figcaption {
view-transition-name: figure-caption;
}
The following code applies a custom animation just to the <figcaption>
:
@keyframes grow-x {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
@keyframes shrink-x {
from {
transform: scaleX(1);
}
to {
transform: scaleX(0);
}
}
::view-transition-group(figure-caption) {
height: auto;
right: 0;
left: auto;
transform-origin: right center;
}
::view-transition-old(figure-caption) {
animation: 0.25s linear both shrink-x;
}
::view-transition-new(figure-caption) {
animation: 0.25s 0.25s linear both grow-x;
}
We create a custom CSS animation and apply it to the ::view-transition-old(figure-caption)
and ::view-transition-new(figure-caption)
pseudo-elements. We also apply other styles to keep them both in the same place and to stop the default styling from interfering with our custom animations.
Using the match-element
value
This example contains a list of technologies–HTML, CSS, SVG, and JS–that are displayed in a sidebar next to a main content area, which starts out empty. Clicking a technology's heading animates its content into the adjoining content area that shows more details.
HTML
The <main>
element contains an unordered list and an <article>
element. The multiple child <li>
elements inside the list each contain an <a>
element inside a heading.
<main class="match-element-applied">
<ul>
<li>
<h2><a href="#">HTML</a></h2>
<h3>HyperText Markup Language</h3>
<p>HyperText Markup Language (HTML) is the most basic building block of the web. It defines the meaning and structure of web content. HTML provides the fundamental building blocks for structuring web documents and apps.
</p>
</li>
<li>
<h2><a href="#">CSS</a></h2>
<h3>Cascading Style Sheets</h3>
<p>Cascading Style Sheets (CSS) is a stylesheet language used to describe the presentation of a document written in HTML or XML (including XML dialects such as SVG, MathML or XHTML). CSS describes how elements should be rendered on screen, on paper, in speech, or on other media.
</p>
</li>
<li>
<h2><a href="#">SVG</a></h2>
<h3>Scalable Vector Graphics</h3>
<p>Scalable Vector Graphics (SVG) is an XML-based markup language for describing two-dimensional based vector graphics.
</p>
</li>
<li>
<h2><a href="#">JS</a></h2>
<h3>JavaScript</h3>
<p>JavaScript (JS) is the web's native programming language. JavaScript is a lightweight, interpreted (or just-in-time compiled) programming language with first-class functions. While it is most well-known as the scripting language for web pages, many non-browser environments, such as Node.js, also use it.
</p>
</li>
</ul>
<article></article>
</main>
CSS
We use flexbox to lay out the <li>
and the <article>
side by side, and to make the list items share equal amount of space in the first column. The list takes up 35% of the container's width, while the <article>
fills the remaining available horizontal space.
main {
container-type: inline-size;
width: 100%;
height: 100%;
display: flex;
gap: 2cqw;
position: relative;
}
ul {
width: 35cqw;
display: flex;
flex-direction: column;
gap: 1cqw;
}
article {
flex: 1;
}
li {
flex: 1;
}
We also define a rule that selects elements with the active-item
class. When this class is applied to an element, the rule causes it to be positioned exactly over the top of the <article>
element. This class will be applied to the list items via JavaScript when their links are clicked, which will initiate a view transition.
.active-item {
position: absolute;
z-index: 1;
translate: 37cqw;
width: calc(100% - 37cqw);
height: 100%;
}
By default, all elements in a view transition are animated together in a single cross-fade. In this example, however, we don't want this — we want each list item to have its own movement animation. We can achieve this by applying view-transition-name: match-element
to every list item:
.match-element-applied li {
view-transition-name: match-element;
}
The match-element-applied
class is applied to the <main>
element by default, which is why the checkbox in the Result frame is initially selected. If you uncheck it, the class is removed and the default cross-fade animation comes into effect instead. You can toggle the checkbox to compare the default animation with the one applied when view-transition-name: match-element
is used.
Next, we customize the animation by using the ::view-transition-group()
pseudo-element to apply an animation-duration
to all the view transition groups (signified by the *
identifier) and give all the old and new snapshots a height
of 100%
. This works around differences in the aspect ratios of the old and new snapshots and makes the animations look smoother:
::view-transition-group(*) {
animation-duration: 0.5s;
}
html::view-transition-old(*),
html::view-transition-new(*) {
height: 100%;
}
JavaScript
In this example, the active-item
class is applied to the list items when their links are clicked; this is achieved via the updateActiveItem()
function:
const mainElem = document.querySelector("main");
let prevElem;
let checkboxElem = document.querySelector("input");
// View transition code
function updateActiveItem(event) {
// Get the list item that contains the clicked link
const clickedElem = event.target.parentElement.parentElement;
// Set the active-item class on the list item
clickedElem.className = "active-item";
// Keep track of the previous item that was clicked, if any.
// Remove the active-item class from the previous item so that only
// one list item is placed over the <article> at any one time
if (prevElem === clickedElem) {
prevElem.className = "";
prevElem = undefined;
} else if (prevElem) {
prevElem.className = "";
prevElem = clickedElem;
} else {
prevElem = clickedElem;
}
}
mainElem.addEventListener("click", (event) => {
event.preventDefault() // Prevent iframe from scrolling when clicked
// Do nothing unless a link is clicked inside the <main> element
if (event.target.tagName !== "A") {
return;
}
// Run updateActiveItem() on its own if view transitions are not supported
if (!document.startViewTransition) {
updateActiveItem(event);
} else {
// Run updateActiveItem() via startViewTransition()
const transition = document.startViewTransition(() =>
updateActiveItem(event),
);
}
});
// Toggle the class on <main> to control whether or not match-element is applied
checkboxElem.addEventListener("change", () => {
mainElem.classList.toggle("match-element-applied");
});
Running the updateActiveItem()
function via the startViewTransition()
function animates the display of technology details smoothly.
Result
Click a technology heading in the sidebar and notice the animation effect of its content into the main content area.
There is also a checkbox, which is selected by default, so view-transition-name: match-element
is applied. Uncheck the checkbox and click a heading again to see how the view-transition works without view-transition-name: match-element
.
Specifications
Specification |
---|
CSS View Transitions Module Level 1 # view-transition-name-prop |