Preview
Open Original
const pages = {
home: {
title: 'View Transitions API',
subtitle: 'Create smooth, native-feeling page transitions with zero libraries',
hero: true,
cards: [
{icon:'โก',title:'Lightning Fast',text:'Native browser API means zero overhead and buttery smooth 60fps animations.',cls:'lightning-fast'},
{icon:'๐จ',title:'Fully Customizable',text:'Control timing, easing, and animation behavior with simple CSS.',cls:'fully-customizable'},
{icon:'๐ฑ',title:'Mobile-First',text:'Feels like a native app on any device. No jank, no lag.',cls:'mobile-first'}
]
},
features: {
title: 'Powerful Features',
subtitle: 'Everything you need for modern web experiences',
hero: false,
cards: [
{icon:'๐',title:'State Morphing',text:'Elements smoothly morph between different states and positions.',cls:'state-morphing'...
const pages = {
home: {
title: 'View Transitions API',
subtitle: 'Create smooth, native-feeling page transitions with zero libraries',
hero: true,
cards: [
{icon:'โก',title:'Lightning Fast',text:'Native browser API means zero overhead and buttery smooth 60fps animations.',cls:'lightning-fast'},
{icon:'๐จ',title:'Fully Customizable',text:'Control timing, easing, and animation behavior with simple CSS.',cls:'fully-customizable'},
{icon:'๐ฑ',title:'Mobile-First',text:'Feels like a native app on any device. No jank, no lag.',cls:'mobile-first'}
]
},
features: {
title: 'Powerful Features',
subtitle: 'Everything you need for modern web experiences',
hero: false,
cards: [
{icon:'๐',title:'State Morphing',text:'Elements smoothly morph between different states and positions.',cls:'state-morphing'},
{icon:'๐ฏ',title:'Named Transitions',text:'Control individual element animations with view-transition-name.',cls:'named-transitions'},
{icon:'๐ ๏ธ',title:'Developer Friendly',text:'Simple API that works with any framework or vanilla JS.',cls:'developer-friendly'},
{icon:'โฟ',title:'Accessible',text:'Respects prefers-reduced-motion and degrades gracefully.',cls:'accessible'}
]
},
about: {
title: 'About This Demo',
subtitle: 'A minimal example of SPA navigation with View Transitions',
hero: false,
cards: [
{icon:'๐',title:'Learn More',text:'Check out the MDN docs for complete API reference and advanced techniques.',cls:'learn-more'},
{icon:'๐ป',title:'Browser Support',text:'Works in Chrome, Edge, Safari, and Firefox (2025). Falls back gracefully.',cls:'browser-support'},
{icon:'๐',title:'Production Ready',text:'Used by major sites for smooth navigation and UI state changes.',cls:'production-ready'}
]
}
};
let currentPage = 'home';
const app = document.getElementById('app');
function renderPage(page, updateHistory = true) {
const data = pages[page];
const fragment = document.createDocumentFragment();
const wrapper = document.createElement('div');
wrapper.className = 'page-content';
let html = `
<div class="page-header">
<h1>${data.title}</h1>
<p>${data.subtitle}</p>
</div>
`;
if (data.hero) {
html += `
<div class="hero">
<h2>Build Better Experiences</h2>
<p>Click around to see smooth page transitions in action</p>
</div>
`;
}
html += '<div class="content">';
data.cards.forEach(c => {
html += `
<div class="card card-${c.cls}">
<div class="icon">${c.icon}</div>
<h3>${c.title}</h3>
<p>${c.text}</p>
</div>
`;
});
html += '</div>';
wrapper.innerHTML = html;
fragment.appendChild(wrapper);
app.innerHTML = '';
app.appendChild(fragment);
if (updateHistory) {
history.pushState({ page }, '', `#${page}`);
}
}
function navigateToPage(page) {
if (page === currentPage) return;
currentPage = page;
document.querySelector('nav a.active')?.classList.remove('active');
document.querySelector(`nav a[data-page="${page}"]`)?.classList.add('active');
if (document.startViewTransition) {
document.startViewTransition(() => renderPage(page));
} else {
renderPage(page);
}
}
document.querySelectorAll('nav a').forEach(link => {
link.addEventListener('click', e => {
e.preventDefault();
navigateToPage(link.dataset.page);
});
});
function handlePopState(e) {
const page = e.state?.page || 'home';
if (page === currentPage) return;
currentPage = page;
document.querySelector('nav a.active')?.classList.remove('active');
document.querySelector(`nav a[data-page="${page}"]`)?.classList.add('active');
if (document.startViewTransition) {
document.documentElement.classList.add('back-transition');
document.startViewTransition(() => renderPage(page, false))
.finished.then(() => document.documentElement.classList.remove('back-transition'));
} else {
renderPage(page, false);
}
}
window.addEventListener('popstate', handlePopState);
const initialHash = location.hash.slice(1) || 'home';
if (pages[initialHash]) {
currentPage = initialHash;
document.querySelector(`nav a[data-page="${initialHash}"]`)?.classList.add('active');
}
renderPage(currentPage, false);
if (!history.state) {
history.replaceState({ page: currentPage }, '', `#${currentPage}`);
}
!