revision:
CSS transitions provide a way to control animation speed when changing CSS properties.
Instead of having property changes take effect immediately, you can cause the changes in a property to take place over a period of time.
For example, if you change the color of an element from white to black, usually the change is instantaneous. With CSS transitions enabled, changes occur at time intervals that follow an acceleration curve, all of which can be customized.
Animations that involve transitioning between two states are often called implicit transitions as the states in between the start and final states are implicitly defined by the browser.
CSS transitions let you decide which properties to animate (by listing them explicitly), when the animation will start (by setting a delay), how long the transition will last (by setting a duration), and how the transition will run (by defining an easing function, e.g., linearly or quick at the beginning, slow at the end).
CSS transitions are controlled using the shorthand "transition" property. This is the best way to configure transitions, as it makes it easier to avoid out of sync parameters. The individual components of the transition can be controlled with the following sub-properties:
transition-property : specifies the name or names of the CSS properties to which transitions should be applied. Only properties listed here are animated during transitions; changes to all other properties occur instantaneously as usual.
transition-duration : specifies the duration over which transitions should occur. You can specify a single duration that applies to all properties during the transition, or multiple values to allow each property to transition over a different period of time.
transition-timing-function : specifies a function to define how intermediate values for properties are computed. Easing functions determine how intermediate values of the transition are calculated. Most easing functions can be specified by providing the graph of the corresponding function, as defined by four points defining a cubic bezier. You can also choose easing from Easing functions cheat sheet.
transition-delay : defines how long to wait between the time a property is changed and the transition actually begins.
The transition shorthand CSS syntax is written as follows: div{transition: [transition-property] [transition-duration] [transition-timing-function] [transition-delay] [transition-behavior];}
Comma separate values can be set to do different transitions on different properties: e.g. div{transition: width 2s, height 4s;}.
/* Apply to 1 property */
/* property name | duration */
transition: margin-right 4s;
/* property name | duration | delay */
transition: margin-right 4s 1s;
/* property name | duration | easing function */
transition: margin-right 4s ease-in-out;
/* property name | duration | easing function | delay */
transition: margin-right 4s ease-in-out 1s;
/* property name | duration | behavior */
transition: display 4s allow-discrete;
/* Apply to 2 properties */
transition:
margin-right 4s,
color 1s;
/* Apply to all changed properties */
transition: all 0.5s ease-out allow-discrete;
transition: 200ms linear 50ms;
/* Global values */
transition: inherit;
transition: initial;
transition: revert;
transition: revert-layer;
transition: unset;
The View Transition API provides a mechanism for easily creating animated transitions between different website views. This includes animating between DOM states in a single-page app (SPA), and animating the navigation between documents in a multi-page app (MPA).
Same-page transitions require JavaScript.
Multi-page transitions require only CSS.
A same-page transition involves and animation when the DOM is changed without the page changing, like a list being sorted.
A multi-page transition is for animating elements between page loads, like a video thumbnail transitioning into a video element.
View transitions are a popular design choice for reducing users' cognitive load, helping them stay in context, and reducing perceived loading latency as they move between states or views of an application.
ViewTransition : represents a view transition, and provides functionality to react to the transition reaching different states (e.g. ready to run the animation, or animation finished) or skip the transition altogether.
Document.startViewTransition() : starts a new same-document (SPA) view transition and returns a ViewTransition object to represent it.
PageRevealEvent : the event object for the pagereveal event. During a cross-document navigation, it allows you to manipulate the related view transition (providing access to the relevant ViewTransition object) from the document being navigated to, if a view transition was triggered by the navigation.
PageSwapEvent : the event object for the pageswap event. During a cross-document navigation, it allows you to manipulate the related view transition (providing access to the relevant ViewTransition object) from the document being navigated from, if a view transition was triggered by the navigation. It also provides access to information on the navigation type and current and destination document history entries.
The Window pagereveal event : fired when a document is first rendered, either when loading a fresh document from the network or activating a document (either from back/forward cache (bfcache) or prerender).
The Window pageswap event : fired when a document is about to be unloaded due to a navigation.
<link rel="expect"> : identifies the most critical content in the associated document for the user's initial view of the page. Document rendering will be blocked until the critical content has been parsed, ensuring a consistent first paint — and therefore, view transition — across all supporting browsers
At rules:
@view-transition : in the case of a cross-document navigation, @view-transition is used to opt in the current and destination documents to undergo a view transition.
Properties:
view-transition-name : provides the selected element with a separate identifying name and causes it to participate in a separate view transition from the root view transition — or no view transition if the none value is specified.
Pseudo elements
::view-transition : the root of the view transitions overlay, which contains all view transitions and sits over the top of all other page content.
::view-transition-group(): the root of a single view transition.
::view-transition-image-pair() : the container for a view transition's old and new views — before and after the transition.
::view-transition-old() : a static snapshot of the old view, before the transition.
::view-transition-new() : a live representation of the new view, after the transition.
basic syntax for a same-page transition:
if (!document.startViewTransition) {
updateTheDOM();
} else {
document.startViewTransition(() => updateTheDOM());
}
for multi-page transition you need this meta tag:
<meta name="view-transition" content="same-origin">
Then any element you want to transition between pages you make sure has a totally unique view-transition-name applied in the styles, on both the outgoing page and incoming page.
code:
<div class="spec-1">
<header view-transition-name="buttons-header">
<button id="add" aria-label="Add List Item in Random Position"
view-transition-name="add-button">+</button>
<button id="remove" aria-label="Remove List Item from Random Position"
view-transition-name="remove-button">-</button>
</header>
<ul id="list">
<!-- these all need unique `view-transition-name`s because they all need to move independantly.
They may move or not move depending on list position.-->
<li style="view-transition-name: item-0">Apple</li>
<li style="view-transition-name: item-1">Banana</li>
<li style="view-transition-name: item-2">Guanabana</li>
<li style="view-transition-name: item-3">Star Fruit</li>
<li style="view-transition-name: item-4">Dragonfruit</li>
</ul>
</div>
<style>
ul {display: grid; gap: 0.5rem; padding: 0; width: max-content;}
ul > li {background: white; padding: 0.5rem 1rem; border-radius: 0.3vw; overflow: hidden; contain: layout;}
ul > li.incoming { animation: 0.6s incoming both;}
::view-transition-old(outgoing) {animation: 1s outgoing both; }
@keyframes outgoing {
0% {translate: 0 0; scale: 1; opacity: 1;}
100% {translate: 100px -50px; scale: 1.2; opacity: 0;}
}
@keyframes incoming {
0% { scale: 1.6; opacity: 0; translate: -100px 0;}
100% {scale: 1; opacity: 1; translate: 0 0;}
}
header {display: flex; gap: 1rem;}
header > button {flex: 1; background: #90a4ae; color: white; border: 0; border-radius: 100px;
line-height: unset;}
header > button:hover, header > button:focus-visible {background: #b0bec5;}
button {font-family: inherit; }
</style>
<script>
const list = document.querySelector("#list");
const addButton = document.querySelector("#add");
const removeButton = document.querySelector("#remove");
let startIndex = 5;
addButton.addEventListener("click", () => {
// Fallback for browsers without `startViewTransition`
if (!document.startViewTransition) {
addItem();
return;
}
// FLIP!
const transition = document.startViewTransition(() => {
addItem();
});
});
removeButton.addEventListener("click", () => {
const randomListItem = getRandomItem();
randomListItem.classList.remove("incoming");
randomListItem.style.viewTransitionName = "outgoing";
if (!document.startViewTransition) {
randomListItem.remove();
return;
}
const transition = document.startViewTransition(() => {
randomListItem.remove();
});
});
function addItem() {
const newListItem = document.createElement("li");
newListItem.innerHTML = faker.random.words();
// new list items need to have a unique `view-transition-name` because they need to move
independantly, like all other list items. They may move or not move depending on
list position.
newListItem.style.viewTransitionName = `item-${++startIndex}`;
newListItem.classList.add("incoming");
const numberOfListItems = list.querySelectorAll(`:scope > li`).length;
const randomPosition = getRandomInt(0, numberOfListItems);
if (randomPosition === 0) {
list.insertAdjacentElement("afterBegin", newListItem);
} else {
const randomListItem = list.querySelector(
`:scope > :nth-child(${randomPosition})`
);
randomListItem.insertAdjacentElement("afterEnd", newListItem);
}
}
function getRandomItem() {
const numberOfListItems = list.querySelectorAll(`:scope > li`).length;
const randomPosition = getRandomInt(1, numberOfListItems);
return list.querySelector(`:scope > :nth-child(${randomPosition})`);
}
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
</script>