CSS View Transitions
CSS View Transitions
The View Transitions API provides a way to create smooth transitions between different states of a webpage, whether it's navigating between pages or updating content within the same page.
Browser Support
View Transitions are supported in modern browsers:
- Chrome 111+
- Edge 111+
- Opera 97+
- Safari 17.2+ (experimental)
- Firefox (in development)
Basic Concepts
View Transition Process
- Capture the old state
- Process the new state
- Transition between states
/* Enable view transitions */
:root {
view-transition-name: none;
}
/* Style specific transitions */
.element {
view-transition-name: element-transition;
}
JavaScript Integration
// Basic page transition
document.startViewTransition(() => {
// Update the DOM here
document.body.innerHTML = newContent;
});
// With async operations
async function updateView() {
const transition = document.startViewTransition(async () => {
const content = await fetchNewContent();
document.body.innerHTML = content;
});
await transition.finished;
}
Practical Examples
Simple Element Transition
.card {
view-transition-name: card;
}
::view-transition-old(card) {
animation: fade-out 300ms ease-out;
}
::view-transition-new(card) {
animation: fade-in 300ms ease-in;
}
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
Page Navigation Transition
// Handle navigation
document.addEventListener('click', e => {
if (e.target.tagName === 'A') {
e.preventDefault();
document.startViewTransition(async () => {
const response = await fetch(e.target.href);
const text = await response.text();
document.body.innerHTML = text;
window.history.pushState({}, '', e.target.href);
});
}
});
Image Gallery Transition
.gallery-image {
view-transition-name: gallery-image;
}
::view-transition-old(gallery-image),
::view-transition-new(gallery-image) {
animation: none;
mix-blend-mode: normal;
}
/* Custom animation for the transition */
::view-transition-image-pair(gallery-image) {
animation: 300ms ease-in-out both image-fade;
}
@keyframes image-fade {
from { opacity: 0; }
to { opacity: 1; }
}
function switchImage(newSrc) {
document.startViewTransition(() => {
const image = document.querySelector('.gallery-image');
image.src = newSrc;
});
}
Advanced Usage
Shared Element Transitions
/* Shared element between views */
.shared-element {
view-transition-name: shared-element;
}
::view-transition-old(shared-element),
::view-transition-new(shared-element) {
animation: none;
mix-blend-mode: normal;
}
::view-transition-image-pair(shared-element) {
animation: 400ms ease-in-out both scale-up;
}
@keyframes scale-up {
from {
transform: scale(0.8);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
Complex State Changes
async function updateComplexView() {
const transition = document.startViewTransition(async () => {
// Remove old content with fade-out
await animateOut('.old-content');
// Update DOM
const newContent = await fetchContent();
updateDOM(newContent);
// Animate new content
await animateIn('.new-content');
});
// Handle transition completion
transition.finished.then(() => {
console.log('Transition complete');
});
}
Custom Transition Styles
/* Define different transitions for different screen sizes */
@media (min-width: 768px) {
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 500ms;
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
}
/* Custom transition for specific elements */
.modal {
view-transition-name: modal;
}
::view-transition-old(modal) {
animation: 300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-out;
}
::view-transition-new(modal) {
animation: 300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-in;
}
Best Practices
1. Performance Optimization
// ✓ Good - Prepare content before transition
async function optimizedTransition() {
const content = await fetchContent(); // Fetch before transition
document.startViewTransition(() => {
updateDOM(content); // Quick DOM update
});
}
// ✗ Bad - Slow transition
document.startViewTransition(async () => {
const content = await fetchContent(); // Delays transition
updateDOM(content);
});
2. Progressive Enhancement
// Check for View Transitions support
if (document.startViewTransition) {
// Use View Transitions
document.startViewTransition(() => updateDOM());
} else {
// Fallback behavior
updateDOM();
}
3. Error Handling
async function safeTransition() {
try {
const transition = document.startViewTransition(async () => {
await updateContent();
});
await transition.finished;
} catch (error) {
console.error('Transition failed:', error);
// Fallback update
await updateContent();
}
}
Common Use Cases
- Page Navigation: Smooth transitions between pages
- Image Galleries: Transitioning between images
- Modal Windows: Animated modal appearances
- Content Updates: Smooth content replacements
- List Modifications: Animating list changes
Tips and Tricks
1. Debugging Transitions
/* Slow down transitions for debugging */
::view-transition-group(*),
::view-transition-image-pair(*),
::view-transition-old(*),
::view-transition-new(*) {
animation-duration: 2s !important;
}
2. Custom Animation Timing
/* Define reusable timing variables */
:root {
--transition-timing: cubic-bezier(0.4, 0, 0.2, 1);
--transition-duration: 300ms;
}
::view-transition-old(custom),
::view-transition-new(custom) {
animation-timing-function: var(--transition-timing);
animation-duration: var(--transition-duration);
}
3. State Management
// Track transition state
let isTransitioning = false;
async function managedTransition() {
if (isTransitioning) return;
isTransitioning = true;
const transition = document.startViewTransition(async () => {
await updateContent();
});
await transition.finished;
isTransitioning = false;
}
CSS View Transitions provide a powerful way to create smooth, engaging transitions in web applications. They enhance the user experience by making state changes and navigation feel more natural and polished.