From harness-claude
Enables native View Transitions API for smooth page animations in Astro, persists interactive islands across navigations, and exposes lifecycle events for custom JS hooks.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Animate page navigations with native browser View Transitions API, persist interactive islands across pages, and hook into the transition lifecycle.
Implements native React View Transition API for smooth animations: page transitions, shared elements, route changes, Suspense reveals, list reorders, and directional navigation in Next.js.
Implements React View Transition API for smooth animations: page transitions, route changes, shared elements, list reorders, enter/exit, and Next.js integration.
Implements Barba.js for smooth page transitions on multi-page sites, enabling SPA-like navigation via AJAX without full reloads. Guides on wrappers, containers, namespaces, lifecycle hooks, and GSAP integration.
Share bugs, ideas, or general feedback.
Animate page navigations with native browser View Transitions API, persist interactive islands across pages, and hook into the transition lifecycle.
<ViewTransitions /> component to your layout's <head>. This is the only thing needed to enable Astro's router and transition support:---
// src/layouts/BaseLayout.astro
import { ViewTransitions } from 'astro:transitions';
---
<html>
<head>
<meta charset="utf-8" />
<ViewTransitions />
</head>
<body>
<slot />
</body>
</html>
transition:name to create a shared-element transition between two pages. The value must be unique on the page and match on both the source and destination page:<!-- Blog index: thumbnail -->
<img src={post.cover} transition:name={`cover-${post.slug}`} alt="" />
<!-- Blog post: hero image — same transition:name value -->
<img src={post.data.cover} transition:name={`cover-${post.slug}`} class="hero" alt="" />
transition:animate. Built-in animations are fade (default), slide, morph, and none:<!-- Slide in from the right -->
<main transition:animate="slide">
<slot />
</main>
<!-- Suppress animation on a specific element -->
<nav transition:animate="none">...</nav>
transition:persist to keep an element alive across page navigations, preventing remount and preserving state:<!-- Audio player that continues playing across pages -->
<audio-player transition:persist="audio-player" src={streamUrl} />
<!-- React island that keeps its internal state -->
<ShoppingCart client:load transition:persist />
document to run code at specific moments:// src/scripts/analytics.js
document.addEventListener('astro:page-load', () => {
// Fires after every page load, including the initial one
// Re-initialize scripts that rely on DOM presence
initAnalytics();
});
document.addEventListener('astro:before-swap', (e) => {
// e.newDocument is the incoming Document object
// Mutate it before the swap — add/remove classes, inject elements
});
document.addEventListener('astro:after-swap', () => {
// DOM has been replaced — re-query elements
highlightActiveNavLink();
});
astro:page-load event instead of DOMContentLoaded for any script that must run on every navigation. DOMContentLoaded only fires on hard page loads:// Wrong: only fires on initial load
document.addEventListener('DOMContentLoaded', init);
// Correct: fires on both initial load and every view transition
document.addEventListener('astro:page-load', init);
data-astro-reload:<!-- Forces a full page reload for this link -->
<a href="/admin" data-astro-reload>Admin Panel</a>
transition:animate with a custom animation object for full control:import { fade } from 'astro:transitions';
// Custom duration
const slowFade = fade({ duration: '0.8s' });
<main transition:animate={slowFade}>...</main>
Astro View Transitions is built on the browser-native View Transitions API (Chrome 111+, with a fallback for other browsers). When <ViewTransitions /> is present, Astro intercepts link clicks, fetches the next page's HTML in the background, and performs a DOM swap — without a full page reload. This is what enables the cross-document transitions.
How the swap works:
astro:before-preparation fires (you can cancel here)astro:before-swap fires — e.newDocument is available for mutationastro:after-swap fires — DOM is now the new pageastro:page-load fires — transition animation is completetransition:persist with framework components:
When a client:load island has transition:persist, Astro moves the existing DOM node to the new page instead of unmounting and remounting it. The island's JavaScript state (React state, Vue reactive data) is preserved. This is how you build a music player or a persistent chat widget.
Fallback for unsupported browsers:
Astro provides an automatic fallback for browsers that do not support the native View Transitions API. By default it uses animate (CSS animation fallback). Set fallback="none" to disable the fallback entirely, or fallback="swap" for an instant DOM swap with no animation.
<ViewTransitions fallback="none" />
Performance considerations:
View Transitions prefetch the next page on hover (configurable). This means navigation latency is typically near-zero. However, the transition itself is GPU-accelerated via the browser — avoid complex CSS transforms on transitioned elements that could cause paint storms.
Scroll behavior:
By default, Astro scrolls to the top on navigation (matching full-page-load behavior). Override this with transition:animate options or by listening to astro:after-swap and calling window.scrollTo() manually.
https://docs.astro.build/en/guides/view-transitions