Day 25: View Transitions
With one day left before wrap-up, I decided to tackle something I’d been eyeing since the beginning: Astro’s View Transitions API. It felt risky—this feature touches every page and could break things in subtle ways. But the potential payoff was too good to ignore: smooth, app-like navigation that makes the site feel polished and intentional.
The Risk Was Real
Adding View Transitions on Day 25 meant touching code that had been stable for weeks. Scroll animations, analytics tracking, React components—all of it needed to play nicely with Astro’s client-side navigation. One wrong move and I’d spend the final day debugging instead of wrapping up.
But sometimes you have to trust the foundation you’ve built. Twenty-four days of tests, property-based verification, and careful architecture gave me the confidence to try.
What Got Implemented
Core View Transitions Setup
The foundation was straightforward: add Astro’s ViewTransitions component to both layouts with fallback="swap" for browsers that don’t support the API natively.
import { ViewTransitions } from 'astro:transitions';
<ViewTransitions fallback="swap" />
Logo Morphing
One of the most satisfying details: the logo now smoothly morphs between header and footer positions during page transitions. Adding transition:name="site-logo" to both logo elements creates a visual thread that ties navigation together.
Scroll Animation Refactoring
This was the biggest undertaking. The existing scroll-triggered animations used IntersectionObservers that initialized once on page load. With View Transitions, pages don’t fully reload—so those observers needed to be reinitializable.
The refactor introduced:
- Module-level state with an
initScrollRevealfunction astro:page-loadlistener to reinitialize animations after navigationastro:before-swaplistener to cleanup observers before page swap- Proper cleanup to prevent memory leaks
Analytics Cleanup
View Transitions introduced a subtle bug risk: event listeners that persist across navigations could fire multiple times or leak memory. New cleanup functions were added:
cleanupExternalLinkTracking()- removes click listeners before page swapcleanupScrollTracking()- cleans up scroll depth tracking
Focus Management
Accessibility during transitions required attention. Astro handles focus automatically when using default ViewTransitions, but I added documentation and property tests to verify:
- No custom swap overrides interfere with focus handling
- Global CSS includes
focus-visiblestyles - Interactive components maintain proper focus styling
React Component Compatibility
The Assessment form and MobileMenu are React islands that hydrate on the client. View Transitions could break their state or prevent re-hydration. E2E tests verified:
- Assessment component re-hydrates correctly after navigation from other pages
- Form state persists during same-page interactions
- MobileMenu open/close state works properly after transitions
- Back navigation doesn’t break component state
The Testing Strategy
Given the risk, testing was comprehensive:
Property-based tests verified:
- ViewTransitions presence in all layouts
- Consistent
transition:namevalues for logos - Reduced motion preferences are respected
- Focus management follows accessibility requirements
E2E tests covered:
- React component re-hydration after navigation
- Form state persistence
- Mobile menu behavior across transitions
- Visual regression at adjusted thresholds
What I Learned
Trust your test suite. The comprehensive testing from earlier days gave me confidence to make this change. Without those property tests and E2E coverage, I wouldn’t have attempted this on Day 25.
View Transitions require lifecycle awareness. Code that runs once on page load needs to be refactored to handle re-initialization. This isn’t just about the feature working—it’s about preventing memory leaks and duplicate event handlers.
Small details compound. The logo morphing effect took minutes to implement but adds polish that users feel even if they can’t articulate why.
Cleanup is as important as setup. Half the work was ensuring things get torn down properly before navigation, not just initialized after.
The Result
The site now feels like an app. Navigation is smooth, the logo creates visual continuity, and none of the existing functionality broke. The scroll animations, analytics, and React components all work exactly as they did before—just with nicer transitions between pages.
Was it risky? Yes. Was it worth it? Absolutely.
Tomorrow we wrap up.