From extended-3d-scroll
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.
npx claudepluginhub freshtechbro/claudedesignskills --plugin extended-3d-scrollThis skill uses the workspace's default tool permissions.
Modern page transition library for creating fluid, smooth transitions between website pages. Barba.js makes multi-page websites feel like Single Page Applications (SPAs) by hijacking navigation and managing transitions without full page reloads.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
Modern page transition library for creating fluid, smooth transitions between website pages. Barba.js makes multi-page websites feel like Single Page Applications (SPAs) by hijacking navigation and managing transitions without full page reloads.
Barba.js is a lightweight (7kb minified and compressed) JavaScript library that intercepts navigation between pages, fetches new content via AJAX, and smoothly transitions between old and new containers. It reduces page load delays and HTTP requests while maintaining the benefits of traditional multi-page architecture.
Core Features:
Barba.js uses a specific DOM structure to manage transitions:
HTML Structure:
<body data-barba="wrapper">
<!-- Static elements (header, nav) stay outside container -->
<header>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</header>
<!-- Dynamic content goes in container -->
<main data-barba="container" data-barba-namespace="home">
<!-- This content changes on navigation -->
<h1>Home Page</h1>
<p>Content that will transition out...</p>
</main>
<!-- Static footer outside container -->
<footer>© 2025</footer>
</body>
Three Key Elements:
Wrapper (data-barba="wrapper")
Container (data-barba="container")
Namespace (data-barba-namespace="home")
Barba.js follows a precise lifecycle for each navigation:
Default Async Flow:
Sync Flow (with sync: true):
Barba provides 11 lifecycle hooks for controlling transitions:
Hook Execution Order:
Initial page load:
beforeOnce → once → afterOnce
Every navigation:
before → beforeLeave → leave → afterLeave →
beforeEnter → enter → afterEnter → after
Hook Types:
barba.hooks.before())Common Hook Use Cases:
beforeLeave - Reset scroll position, prepare animationsleave - Animate current page outafterLeave - Clean up old pagebeforeEnter - Prepare new page (hide elements, set initial states)enter - Animate new page inafterEnter - Initialize page scripts, analytics trackingViews are page-specific logic containers that run based on namespace:
barba.init({
views: [{
namespace: 'home',
beforeEnter() {
// Home-specific setup
console.log('Entering home page');
},
afterEnter() {
// Initialize home page features
initHomeSlider();
}
}, {
namespace: 'product',
beforeEnter() {
console.log('Entering product page');
},
afterEnter() {
initProductGallery();
}
}]
});
Installation:
npm install --save-dev @barba/core
# or
yarn add @barba/core --dev
Minimal Configuration:
import barba from '@barba/core';
barba.init({
transitions: [{
name: 'default',
leave({ current }) {
// Fade out current page
return gsap.to(current.container, {
opacity: 0,
duration: 0.5
});
},
enter({ next }) {
// Fade in new page
return gsap.from(next.container, {
opacity: 0,
duration: 0.5
});
}
}]
});
Classic fade-out, fade-in transition:
import barba from '@barba/core';
import gsap from 'gsap';
barba.init({
transitions: [{
name: 'fade',
async leave({ current }) {
await gsap.to(current.container, {
opacity: 0,
duration: 0.5,
ease: 'power2.inOut'
});
},
async enter({ next }) {
// Start invisible
gsap.set(next.container, { opacity: 0 });
// Fade in
await gsap.to(next.container, {
opacity: 1,
duration: 0.5,
ease: 'power2.inOut'
});
}
}]
});
Simultaneous fade between pages:
barba.init({
transitions: [{
name: 'crossfade',
sync: true, // Enable sync mode
leave({ current }) {
return gsap.to(current.container, {
opacity: 0,
duration: 0.8,
ease: 'power2.inOut'
});
},
enter({ next }) {
return gsap.from(next.container, {
opacity: 0,
duration: 0.8,
ease: 'power2.inOut'
});
}
}]
});
Slide old page out, new page in with overlap:
barba.init({
transitions: [{
name: 'slide',
sync: true,
leave({ current }) {
return gsap.to(current.container, {
x: '-100%',
duration: 0.7,
ease: 'power3.inOut'
});
},
enter({ next }) {
// Start off-screen right
gsap.set(next.container, { x: '100%' });
// Slide in from right
return gsap.to(next.container, {
x: '0%',
duration: 0.7,
ease: 'power3.inOut'
});
}
}]
});
Define different transitions based on navigation context:
barba.init({
transitions: [
// Home to any page: fade
{
name: 'from-home-fade',
from: { namespace: 'home' },
leave({ current }) {
return gsap.to(current.container, {
opacity: 0,
duration: 0.5
});
},
enter({ next }) {
return gsap.from(next.container, {
opacity: 0,
duration: 0.5
});
}
},
// Product to product: slide left
{
name: 'product-to-product',
from: { namespace: 'product' },
to: { namespace: 'product' },
leave({ current }) {
return gsap.to(current.container, {
x: '-100%',
duration: 0.6
});
},
enter({ next }) {
gsap.set(next.container, { x: '100%' });
return gsap.to(next.container, {
x: '0%',
duration: 0.6
});
}
},
// Default fallback
{
name: 'default',
leave({ current }) {
return gsap.to(current.container, {
opacity: 0,
duration: 0.3
});
},
enter({ next }) {
return gsap.from(next.container, {
opacity: 0,
duration: 0.3
});
}
}
]
});
Use @barba/router for route-specific transitions:
Installation:
npm install --save-dev @barba/router
Usage:
import barba from '@barba/core';
import barbaPrefetch from '@barba/prefetch';
import barbaRouter from '@barba/router';
// Define routes
barbaRouter.init({
routes: [
{ path: '/', name: 'home' },
{ path: '/about', name: 'about' },
{ path: '/products/:id', name: 'product' }, // Dynamic segment
{ path: '/blog/:category/:slug', name: 'blog-post' }
]
});
barba.use(barbaRouter);
barba.use(barbaPrefetch); // Optional: prefetch on hover
barba.init({
transitions: [{
name: 'product-transition',
to: { route: 'product' }, // Trigger on route name
leave({ current }) {
return gsap.to(current.container, {
scale: 0.95,
opacity: 0,
duration: 0.5
});
},
enter({ next }) {
return gsap.from(next.container, {
scale: 1.05,
opacity: 0,
duration: 0.5
});
}
}]
});
Show loading state during page fetch:
barba.init({
transitions: [{
async leave({ current }) {
// Show loader
const loader = document.querySelector('.loader');
gsap.set(loader, { display: 'flex', opacity: 0 });
gsap.to(loader, { opacity: 1, duration: 0.3 });
// Fade out page
await gsap.to(current.container, {
opacity: 0,
duration: 0.5
});
},
async enter({ next }) {
// Hide loader
const loader = document.querySelector('.loader');
await gsap.to(loader, { opacity: 0, duration: 0.3 });
gsap.set(loader, { display: 'none' });
// Fade in page
await gsap.from(next.container, {
opacity: 0,
duration: 0.5
});
}
}]
});
Barba.js works seamlessly with GSAP for animations:
Timeline-Based Transitions:
import barba from '@barba/core';
import gsap from 'gsap';
barba.init({
transitions: [{
async leave({ current }) {
const tl = gsap.timeline();
tl.to(current.container.querySelector('h1'), {
y: -50,
opacity: 0,
duration: 0.3
})
.to(current.container.querySelector('.content'), {
y: -30,
opacity: 0,
duration: 0.3
}, '-=0.2')
.to(current.container, {
opacity: 0,
duration: 0.2
});
await tl.play();
},
async enter({ next }) {
const tl = gsap.timeline();
// Set initial states
gsap.set(next.container, { opacity: 0 });
gsap.set(next.container.querySelector('h1'), { y: 50, opacity: 0 });
gsap.set(next.container.querySelector('.content'), { y: 30, opacity: 0 });
tl.to(next.container, {
opacity: 1,
duration: 0.2
})
.to(next.container.querySelector('h1'), {
y: 0,
opacity: 1,
duration: 0.5,
ease: 'power3.out'
})
.to(next.container.querySelector('.content'), {
y: 0,
opacity: 1,
duration: 0.5,
ease: 'power3.out'
}, '-=0.3');
await tl.play();
}
}]
});
Reference gsap-scrolltrigger skill for advanced GSAP integration patterns.
Initialize libraries or scripts per page:
barba.init({
views: [
{
namespace: 'home',
afterEnter() {
// Initialize home page features
initHomepageSlider();
initParallaxEffects();
},
beforeLeave() {
// Clean up
destroyHomepageSlider();
}
},
{
namespace: 'gallery',
afterEnter() {
initLightbox();
initMasonry();
},
beforeLeave() {
destroyLightbox();
}
}
]
});
Track page views on navigation:
barba.hooks.after(() => {
// Google Analytics
if (typeof gtag !== 'undefined') {
gtag('config', 'GA_MEASUREMENT_ID', {
page_path: window.location.pathname
});
}
// Or use data layer
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'pageview',
page: window.location.pathname
});
});
Re-run scripts after page transitions:
barba.hooks.after(() => {
// Re-initialize third-party widgets
if (typeof twttr !== 'undefined') {
twttr.widgets.load(); // Twitter widgets
}
if (typeof FB !== 'undefined') {
FB.XFBML.parse(); // Facebook widgets
}
// Re-run syntax highlighting
if (typeof Prism !== 'undefined') {
Prism.highlightAll();
}
});
Use @barba/prefetch to load pages on hover:
npm install --save-dev @barba/prefetch
import barba from '@barba/core';
import barbaPrefetch from '@barba/prefetch';
barba.use(barbaPrefetch);
barba.init({
// Prefetch fires on link hover by default
prefetch: {
root: null, // Observe all links
timeout: 3000 // Cache timeout in ms
}
});
Set container min-height to prevent content jump:
[data-barba="container"] {
min-height: 100vh;
/* Or use viewport height minus header/footer */
min-height: calc(100vh - 80px - 60px);
}
Use GPU-accelerated properties:
// ✅ Good - GPU accelerated
gsap.to(element, {
opacity: 0,
x: -100,
scale: 0.9,
rotation: 45
});
// ❌ Avoid - causes reflow/repaint
gsap.to(element, {
width: '50%',
height: '300px',
top: '100px'
});
Remove listeners in beforeLeave or view hooks:
barba.init({
views: [{
namespace: 'home',
afterEnter() {
// Add listeners
this.clickHandler = () => console.log('clicked');
document.querySelector('.btn').addEventListener('click', this.clickHandler);
},
beforeLeave() {
// Remove listeners
document.querySelector('.btn').removeEventListener('click', this.clickHandler);
}
}]
});
Defer image loading until after transition:
barba.init({
transitions: [{
async enter({ next }) {
// Complete transition first
await gsap.from(next.container, {
opacity: 0,
duration: 0.5
});
// Then load images
const images = next.container.querySelectorAll('img[data-src]');
images.forEach(img => {
img.src = img.dataset.src;
img.removeAttribute('data-src');
});
}
}]
});
Problem: Transitions complete instantly without waiting for animations.
Solution: Always return promises or use async/await:
// ❌ Wrong - animation starts but doesn't wait
leave({ current }) {
gsap.to(current.container, { opacity: 0, duration: 0.5 });
}
// ✅ Correct - returns promise
leave({ current }) {
return gsap.to(current.container, { opacity: 0, duration: 0.5 });
}
// ✅ Also correct - async/await
async leave({ current }) {
await gsap.to(current.container, { opacity: 0, duration: 0.5 });
}
Problem: Some links cause full page reloads.
Solution: Barba automatically prevents default on internal links, but you may need to exclude external links:
barba.init({
prevent: ({ href }) => {
// Allow external links
if (href.indexOf('http') > -1 && href.indexOf(window.location.host) === -1) {
return true;
}
return false;
}
});
Problem: Old page CSS affects new page layout during transition.
Solution: Use namespace-specific CSS or reset styles:
/* Namespace-specific styles */
[data-barba-namespace="home"] .hero {
background: blue;
}
[data-barba-namespace="about"] .hero {
background: red;
}
Or reset in beforeEnter:
beforeEnter({ next }) {
// Reset scroll position
window.scrollTo(0, 0);
// Reset any global state
document.body.classList.remove('menu-open');
}
Problem: Page title and meta tags don't update on navigation.
Solution: Use @barba/head plugin or update manually:
npm install --save-dev @barba/head
import barba from '@barba/core';
import barbaHead from '@barba/head';
barba.use(barbaHead);
barba.init({
// Head plugin automatically updates <head> tags
});
Or manually:
barba.hooks.after(({ next }) => {
// Update title
document.title = next.html.querySelector('title').textContent;
// Update meta tags
const newMeta = next.html.querySelectorAll('meta');
newMeta.forEach(meta => {
const name = meta.getAttribute('name') || meta.getAttribute('property');
if (name) {
const existing = document.querySelector(`meta[name="${name}"], meta[property="${name}"]`);
if (existing) {
existing.setAttribute('content', meta.getAttribute('content'));
}
}
});
});
Problem: New page flashes visible before enter animation starts.
Solution: Set initial invisible state in CSS or beforeEnter:
/* CSS approach */
[data-barba="container"] {
opacity: 0;
}
[data-barba="container"].is-visible {
opacity: 1;
}
// JavaScript approach
beforeEnter({ next }) {
gsap.set(next.container, { opacity: 0 });
}
Problem: Sync transitions cause layout shift as containers stack.
Solution: Position containers absolutely during transition:
[data-barba="wrapper"] {
position: relative;
}
[data-barba="container"] {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
Or manage in JavaScript:
barba.init({
transitions: [{
sync: true,
beforeLeave({ current }) {
gsap.set(current.container, {
position: 'absolute',
top: 0,
left: 0,
width: '100%'
});
}
}]
});
This skill includes:
Executable utilities for common Barba.js tasks:
transition_generator.py - Generate transition boilerplate codeproject_setup.py - Initialize Barba.js project structureDetailed documentation:
api_reference.md - Complete Barba.js API (hooks, transitions, views, router)hooks_guide.md - All 11 hooks with execution order and use casesgsap_integration.md - GSAP animation patterns for Barba transitionstransition_patterns.md - Common transition implementationsTemplates and starter projects:
starter_barba/ - Complete Barba.js + GSAP starter templateexamples/ - Real-world transition implementations