View Transitions
Smooth native animations for content swaps
Smooth, native animations for content swaps using the View Transitions API.
Enabling Transitions
View Transitions are enabled by default. To explicitly control them:
<!-- Enable globally --><html data-transitions> <!-- Disable for specific section --><div data-transitions="false"> ...</div> <!-- Via meta tag --><meta name="htmlstar:transitions" content="true">
How It Works
When a swap occurs and View Transitions are enabled:
- html★ calls
document.startViewTransition() - Browser captures a screenshot of the old state
- Content is swapped
- Browser captures the new state
- CSS animations run between the two states
CSS for Transitions
Use view-transition-name to identify elements that should animate:
/* Give the element a transition name */#main { view-transition-name: main-content;} /* Animate the old state (exiting) */::view-transition-old(main-content) { animation: fade-out 0.2s ease-out;} /* Animate the new state (entering) */::view-transition-new(main-content) { animation: fade-in 0.2s ease-in;} @keyframes fade-out { to { opacity: 0; }} @keyframes fade-in { from { opacity: 0; }}
Animation Examples
Slide
::view-transition-old(content) { animation: slide-out 0.2s ease-out;} ::view-transition-new(content) { animation: slide-in 0.2s ease-in;} @keyframes slide-out { to { opacity: 0; transform: translateX(-20px); }} @keyframes slide-in { from { opacity: 0; transform: translateX(20px); }}
Scale
::view-transition-old(content) { animation: scale-out 0.15s ease-out;} ::view-transition-new(content) { animation: scale-in 0.15s ease-in;} @keyframes scale-out { to { opacity: 0; transform: scale(0.95); }} @keyframes scale-in { from { opacity: 0; transform: scale(1.05); }}
Crossfade (Simple)
::view-transition-old(content),::view-transition-new(content) { animation-duration: 0.2s;} /* Default crossfade is built-in! */
Multiple Elements
Different elements can have different transitions:
header { view-transition-name: header; }main { view-transition-name: main; }aside { view-transition-name: sidebar; } /* Header stays put */::view-transition-old(header),::view-transition-new(header) { animation: none;} /* Main content slides */::view-transition-old(main) { animation: slide-left-out 0.2s;} ::view-transition-new(main) { animation: slide-left-in 0.2s;}
CSS Transitions Fallback
When the View Transitions API is unavailable, html★ automatically falls back to CSS class-based transitions. No configuration needed — it just works.
How the Fallback Works
.htmlstar-swappingis added to the target element (triggers fade-out)- html★ waits for the CSS transition to complete
- Content is swapped
.htmlstar-swappingis removed,.htmlstar-settlingis added (triggers fade-in)- html★ waits for the settle transition
.htmlstar-settlingis removed
Default Styles
html★ injects default fallback styles automatically:
/* Injected by html★ */.htmlstar-swapping { opacity: 0; transition: opacity 150ms ease-out;}.htmlstar-settling { opacity: 1; transition: opacity 150ms ease-in;}
Custom Fallback Animations
Override the defaults in your own CSS for custom fallback effects:
/* Slide down instead of fade */.htmlstar-swapping { opacity: 0; transform: translateY(-10px); transition: opacity 200ms ease-out, transform 200ms ease-out;}.htmlstar-settling { opacity: 1; transform: translateY(0); transition: opacity 200ms ease-in, transform 200ms ease-in;}
The fallback ensures graceful transitions in all browsers, while browsers with View Transitions API support get the full native experience.
Browser Support
| Browser | View Transitions API | CSS Fallback |
|---|---|---|
| Chrome | 111+ | ✓ |
| Edge | 111+ | ✓ |
| Safari | 18+ | ✓ |
| Firefox | 133+ | ✓ |
Graceful Degradation: All modern browsers now support the native View Transitions API. In older browsers, html★ automatically uses the CSS class-based fallback described above — smooth transitions everywhere, no errors, no broken layouts.
Disabling for Specific Swaps
<!-- This swap won't animate --><button data-href="/api/save" data-target="#status" data-transitions="false"> Save</button>