View Transitions API ¶
Smooth, animated transitions between pages and views using the modern View Transitions API. This implementation provides a polished user experience with automatic transitions for all internal navigation.
Quick Start ¶ #
View Transitions are enabled by default. No configuration required!
To customize behavior, add to your markata.toml:
[view_transitions]
enabled = true
debug = false
See Configuration Reference for all options.
What It Does ¶ #
The View Transitions API automatically adds smooth animations when users navigate between pages:
- Wikilinks (
[[slug]]) - Smooth transitions between related content - Card clicks - Polished navigation from feeds to full posts
- Navigation links - Stable header/footer during transitions
- Post navigation - Smooth prev/next post transitions
- Breadcrumbs - Contextual navigation animations
- All internal links - Any
<a href>to same-origin pages
Smart Exclusions ¶ #
These link types are automatically skipped (use normal navigation):
- External links (
target="_blank") - Download links
- TOC/anchor links (use smooth scroll instead)
- HTMX links (handle their own updates)
- Links with
data-no-transitionattribute
How It Works ¶ #
1. Link Interception ¶ #
A global event listener intercepts clicks on internal links:
// User clicks an internal link
document.addEventListener('click', (e) => {
const link = e.target.closest('a');
if (shouldTransition(link)) {
e.preventDefault();
// Start view transition...
}
});
2. Content Fetching ¶ #
The new page is fetched via the Fetch API:
const response = await fetch(url);
const html = await response.text();
const newDoc = new DOMParser().parseFromString(html, 'text/html');
3. Transition Animation ¶ #
The browser animates between old and new content:
document.startViewTransition(() => {
// Update DOM
document.body.innerHTML = newDoc.body.innerHTML;
document.title = newDoc.title;
history.pushState(null, '', url);
});
4. Script Re-initialization ¶ #
After transition, page scripts are re-initialized:
// Dispatch event for scripts to listen to
window.dispatchEvent(new CustomEvent('view-transition-complete'));
// Scripts automatically re-initialize
if (window.initTooltips) window.initTooltips();
if (window.initScrollSpy) window.initScrollSpy();
Browser Support ¶ #
- ✅ Chrome/Edge 111+ - Full support
- ✅ Safari 18+ (macOS 15+, iOS 18+) - Full support
- ✅ Opera 97+ - Full support
- ⚠️ Firefox - In development
Graceful degradation: On unsupported browsers, navigation works normally without transitions.
Default Animation ¶ #
The default transition is a fade + slide-up effect:
- Old content: Fades out (250ms)
- New content: Fades in + slides up from 20px below (300ms)
- Navigation: Stays stable (200ms minimal animation)
CSS Implementation ¶ #
/* Assign transition names */
.post-content,
.posts-list {
view-transition-name: main-content;
}
/* Animate old content out */
::view-transition-old(main-content) {
animation: fade-out 0.25s ease-out;
}
/* Animate new content in */
::view-transition-new(main-content) {
animation: fade-in 0.3s ease-in, slide-up 0.3s ease-out;
}
@keyframes fade-out {
to { opacity: 0; }
}
@keyframes fade-in {
from { opacity: 0; }
}
@keyframes slide-up {
from { transform: translateY(20px); }
}
Customization ¶ #
See Configuration Reference for complete customization options.
Quick Examples ¶ #
Enable debug logging:
[view_transitions]
debug = true
Skip transitions for specific classes:
[view_transitions]
skip_classes = ["instant-nav", "no-animation"]
Disable globally:
[view_transitions]
enabled = false
Per-link opt-out:
<a href="/page/" data-no-transition>Skip transition</a>
Custom CSS Animations ¶ #
Override the default animations in your custom CSS:
Different Animation Style ¶ #
/* Slide from right instead of bottom */
::view-transition-new(main-content) {
animation: fade-in 0.3s, slide-from-right 0.3s;
}
@keyframes slide-from-right {
from {
opacity: 0;
transform: translateX(30px);
}
}
Faster Transitions ¶ #
::view-transition-old(main-content),
::view-transition-new(main-content) {
animation-duration: 0.2s;
}
Per-Element Transitions ¶ #
/* Different animation for post titles */
.post-title {
view-transition-name: post-title;
}
::view-transition-new(post-title) {
animation: fade-in 0.3s, scale-up 0.3s;
}
@keyframes scale-up {
from { transform: scale(0.95); }
}
Individual Card Morphing ¶ #
Give each card a unique transition name:
<article class="card" style="view-transition-name: card-{{ post.slug }};">
...
</article>
Now cards will morph smoothly when navigating!
Performance ¶ #
- Bundle size: ~8KB unminified JavaScript
- Overhead: Near-zero (exits early on unsupported browsers)
- Animation: GPU-accelerated
- Network: Uses standard
fetch()API - Memory: One in-flight request at a time
Performance Tips ¶ #
- Keep pages small - Large HTML takes longer to parse
- Optimize images - Images in new content delay transition
- Minimize inline scripts - Scripts need re-initialization
- Use caching - Browser caches reduce fetch time
Accessibility ¶ #
View Transitions respect user preferences:
Reduced Motion ¶ #
Users who prefer reduced motion automatically get instant transitions:
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}
Screen Readers ¶ #
- Page title updates immediately
- Main content landmark is preserved
- Focus management handled automatically
- No ARIA changes needed
Troubleshooting ¶ #
Transitions Not Working ¶ #
- Check browser support - Only Chrome 111+, Safari 18+, Opera 97+
- Check console - Look for errors or warnings
- Enable debug mode - Set
debug = truein config - Verify enabled - Check
window.VIEW_TRANSITIONS_CONFIG.enabled
Specific Links Not Transitioning ¶ #
- External link? - Links with
target="_blank"are skipped - HTMX link? - Links with
hx-getare skipped - TOC link? - Anchor links use smooth scroll
- Custom skip rule? - Check
skip_classesandskip_selectors - Non-HTML file? - URLs like
.md,.txt,.xml,.jsonuse native browser navigation
Enable debug mode to see why links are skipped:
[view_transitions]
debug = true
Then check browser console when clicking links.
Scripts Not Working After Transition ¶ #
Make sure your custom scripts listen for the re-initialization event:
function myInit() {
// Your initialization code
}
// Run on initial page load
myInit();
// Re-run after view transitions
window.addEventListener('view-transition-complete', myInit);
Transition Too Fast/Slow ¶ #
Adjust animation duration in CSS:
::view-transition-old(main-content),
::view-transition-new(main-content) {
animation-duration: 0.5s; /* Slower */
}
Advanced Usage ¶ #
Transition Types ¶ #
Add custom data attributes to control transitions per-route:
// Future enhancement - not yet implemented
link.dataset.transitionType = 'slide';
Prefetching ¶ #
Prefetch pages on hover for instant transitions:
// Future enhancement - not yet implemented
link.addEventListener('mouseenter', () => {
fetch(link.href); // Prefetch
});
Loading States ¶ #
Show loading indicator during fetch:
// Future enhancement - not yet implemented
document.addEventListener('htmx:beforeRequest', () => {
showLoadingSpinner();
});
Examples ¶ #
Simple Fade ¶ #
::view-transition-old(main-content) {
animation: fade-out 0.3s;
}
::view-transition-new(main-content) {
animation: fade-in 0.3s;
}
Scale Transition ¶ #
::view-transition-old(main-content) {
animation: scale-down 0.3s;
}
::view-transition-new(main-content) {
animation: scale-up 0.3s;
}
@keyframes scale-down {
to { transform: scale(0.95); opacity: 0; }
}
@keyframes scale-up {
from { transform: scale(0.95); opacity: 0; }
}
Slide Transition ¶ #
::view-transition-old(main-content) {
animation: slide-out-left 0.3s;
}
::view-transition-new(main-content) {
animation: slide-in-right 0.3s;
}
@keyframes slide-out-left {
to { transform: translateX(-100%); opacity: 0; }
}
@keyframes slide-in-right {
from { transform: translateX(100%); opacity: 0; }
}
Related Documentation ¶ #
- Configuration Reference - All configuration options
- Performance - Performance optimization guide
- [[keyboard-navigation|Keyboard Navigation]] - Accessibility features
- Themes - Customizing appearance
Resources ¶ #
Implementation Files ¶ #
- JavaScript:
pkg/themes/default/static/js/view-transitions.js - CSS:
pkg/themes/default/static/css/components.css - Template:
pkg/themes/default/templates/base.html