View Transitions API
Smooth, native page transitions using the browser's View Transitions API.
Overview
MiniWork uses the View Transitions API to create smooth animations between page navigations without a full page reload. This provides a native app-like experience.
Automatic
Transitions happen automatically on link clicks
Directional
Vertical slides for sidebar navigation
Horizontal
Horizontal slides for section changes
60fps
GPU-accelerated CSS animations
How It Works
- User clicks a link
- JavaScript intercepts the click and determines direction
- document.startViewTransition() captures the old state
- New page content is fetched and swapped
- CSS animations transition between old and new states
Direction Detection
The transition direction is determined by navigation context:
| Navigation | Direction |
|---|---|
| Admin: Dashboard → Users → Roles → Settings | Down (vertical) |
| Admin: Settings → Dashboard | Up (vertical) |
| Docs: Getting Started → Core → API | Down (vertical) |
| Home → Docs → Admin | Forward (horizontal) |
| Admin → Home | Back (horizontal) |
CSS Implementation
/* Define transition names for different areas */
.admin-sidebar { view-transition-name: admin-sidebar; }
.admin-header { view-transition-name: admin-header; }
.admin-content { view-transition-name: admin-content; }
/* Static elements - no animation */
::view-transition-old(admin-sidebar),
::view-transition-new(admin-sidebar) {
animation: none;
}
/* Vertical slide animations for content */
@keyframes slide-down-out {
to { transform: translateY(30px); opacity: 0; }
}
@keyframes slide-down-in {
from { transform: translateY(-30px); opacity: 0; }
}
html.vt-down::view-transition-old(admin-content) {
animation: 0.15s ease-out both slide-down-out;
}
html.vt-down::view-transition-new(admin-content) {
animation: 0.15s ease-out both slide-down-in;
}
JavaScript Handler
document.addEventListener('click', async (e) => {
const link = e.target.closest('a[href]');
if (!link || !document.startViewTransition) return;
e.preventDefault();
const direction = getDirection(currentPath, link.href);
document.documentElement.classList.add('vt-' + direction);
const transition = document.startViewTransition(async () => {
const html = await fetch(link.href).then(r => r.text());
// Swap content...
});
await transition.finished;
document.documentElement.classList.remove('vt-' + direction);
});
View Transitions API is supported in Chrome 111+, Edge 111+, and Safari 18+. The code includes fallbacks for unsupported browsers.
Cross-Section Navigation Gotcha
When an element has a view-transition-name but doesn't exist on the target page, the browser creates a transition group with only the 'old' state. This causes a glitchy fade-out effect even if animation: none is set.
This commonly occurs when navigating from a page with a sidebar (docs, admin) to a page without one (landing page). The sidebar fades out late and looks broken.
The Fix: Remove view-transition-name Before Transition
Before starting the view transition, detect cross-section navigation and remove the view-transition-name from elements that won't exist on the target page:
// Before document.startViewTransition()
const isLeavingDocs = currentPath.startsWith('/docs') && !toPath.startsWith('/docs');
const isLeavingAdmin = currentPath.startsWith('/admin') && !toPath.startsWith('/admin');
if (isLeavingDocs) {
const sidebar = document.querySelector('.docs-sidebar');
if (sidebar) sidebar.style.viewTransitionName = 'none';
}
if (isLeavingAdmin) {
const sidebar = document.querySelector('.admin-sidebar');
const header = document.querySelector('.admin-header');
const footer = document.querySelector('.admin-footer');
if (sidebar) sidebar.style.viewTransitionName = 'none';
if (header) header.style.viewTransitionName = 'none';
if (footer) footer.style.viewTransitionName = 'none';
}
// Now start the transition
const transition = document.startViewTransition(async () => {
// fetch and swap content...
});
Setting viewTransitionName to 'none' removes the element from its own transition group. It becomes part of the parent .page-content transition instead, resulting in a smooth unified animation.
Remember to add this fix to both the click event handler and the popstate handler (for browser back/forward navigation).