Animations & Page Transitions
Two Web Components that together handle every animation need in a SmartComponents Django project. SmartMotion owns page-to-page transitions using Barba.js — it intercepts link clicks, runs a visual transition, swaps the content, and fires lifecycle events. SmartEffects owns element-level animations using Anime.js — it listens for those same lifecycle events to re-animate content on every navigation, and also handles scroll-triggered, hover, click, and manual animations independently.
Both components are zero-config in base.html — drop in two tags and every page in
your project gets fluid transitions and entrance animations automatically.
<script type="module" src="smart_motion.js"></script>
<script type="module" src="smart_effect.js"></script>
base.html Setup
Both components live outside the Barba container — they must survive every page transition.
The Barba wrapper, container, global singletons, and both animation components are the only
elements that should be outside .
<body> <!-- Barba wrapper — wraps everything --> <div data-barba="wrapper"> <div data-barba="container" data-barba-namespace="default > <!-- Global singletons — stay here, never re-mount on nav --> <smart-toast position="top-right" max="5"></smart-toast> <smart-modal></smart-modal> <smart-loader type="overlay"></smart-loader>" -- </div> </div> <!-- Animation components — outside container, survive every transition --> <smart-motion type="panel" duration="400"></smart-motion> <smart-effects auto></smart-effects> <!-- CDN deps --> <script src="https://cdn.jsdelivr.net/npm/@barba/core"></script> <script src="https://cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js"></script> <!-- Components --> <script type="module" src="< static 'resources/js/components/smart_motion.js' >"></script> <script type="module" src="< static 'resources/js/components/smart_effect.js' >"></script> </body>
data-barba="container" element on every navigation. Anything inside that element
is destroyed and re-created. SmartMotion and SmartEffects must persist across navigations —
they hold the Barba instance, the overlay DOM element, scroll observers, and event listeners.
Place them directly in <body> after the wrapper.
SmartMotion
Initializes Barba.js, intercepts all link clicks on the page, runs the chosen transition
between pages, re-executes page scripts in the new container, swaps
< block extra_head > styles, and fires three window events for other
components to hook into.
<script> tags found inside the new container. CDN scripts (cross-origin)
are loaded once and cached — never re-fetched. Local/same-origin scripts are re-executed
on every navigation so their DOMContentLoaded and init logic runs fresh.
Inline scripts always re-run, wrapped in an IIFE for scope isolation.
Transition Types
Set type on <smart-motion>. The type is fixed at
initialization — it applies to every navigation for the session.
<!-- Pick one type — applies to every page transition --> <smart-motion type="overlay" duration="400"></smart-motion> <smart-motion type="fade" duration="300"></smart-motion> <smart-motion type="slide" duration="350"></smart-motion> <smart-motion type="scale" duration="400"></smart-motion> <smart-motion type="panel" duration="400"></smart-motion> // duration controls each half of the transition. // Panel total = ~duration * 1.3ms (leave + enter overlap slightly)
Lifecycle Events
SmartMotion dispatches three events on window during every navigation.
SmartEffects listens to smart-page-enter automatically. Your own page scripts
can listen to all three.
| Event | detail | When it fires |
|---|---|---|
| smart-page-leave | { namespace } | When the current page begins leaving — transition starts, old container still visible. |
| smart-page-enter | { namespace, container } | After the new container is fully mounted and scripts have re-run. Safe to query DOM. SmartEffects listens here. |
| smart-page-mounted | { namespace, container } | Fired immediately after smart-page-enter. Convenience alias for code that needs a separate listener. |
// Listen from any script — e.g. re-init a third-party widget after navigation window.addEventListener('smart-page-enter', (e) => { const { namespace, container } = e.detail; console.log('Navigated to:', namespace); // container is the new data-barba="container" element // Safe to query DOM here — scripts have already re-run container.querySelectorAll('[data-chart]').forEach(el => initChart(el)); }); window.addEventListener('smart-page-leave', (e) => { // Tear down things before the page exits destroyWebSockets(); }); // Also available: barba:afterEnter (back-compat alias fired by SmartMotion) window.addEventListener('barba:afterEnter', (e) => { const container = e.detail.container; });
Preventing Transitions
SmartMotion automatically skips Barba for same-page anchor links, external URLs, and hash-only
hrefs. Use data-no-barba on any link to opt it out explicitly.
<!-- These are skipped automatically --> <a href="#section">Anchor — no transition</a> <a href="https://external.com">External — no transition</a> <!-- Opt out individual links --> <a href="/download/" data-no-barba>Force full page load</a> <!-- Barba namespace per page — optional, used in smart-page-enter detail --> < block namespace >home< endblock > <!-- In the template: --> <div data-barba="container" data-barba-namespace="< block namespace >default< endblock >"> // In views.py — pass namespace for analytics or per-namespace effects def dashboard(request): return render(request, 'dashboard.html')
SmartMotion Reference
| Attribute | Type | Description | Default |
|---|---|---|---|
| type | string | overlay · fade · slide · scale · panel. Applies to every page navigation. |
overlay |
| duration | number | Duration in ms for each half of the transition (leave or enter). Panel type uses this value for both phases with slight overlap. | 400 |
Global helpers exposed on window by SmartMotion for page scripts:
| Function | Description |
|---|---|
| window.barbaExecuteScripts(container?) | Manually trigger SmartMotion's post-navigation setup — re-runs scripts, reinits Bootstrap, fires smart-page-enter. Useful after programmatic DOM injection. |
| window.barbaCleanup(names[]) | Delete named globals from window before the next page loads. Pass variable names from the current page that would otherwise leak. |
SmartEffects
Anime.js animation engine as a Web Component. Declare what to animate, when to animate it,
and with which preset or custom parameters — all via HTML attributes. Works standalone or
coordinated with SmartMotion. On every navigation, SmartEffects receives
smart-page-enter, clears animated flags on the new container, and re-fires
all animations on fresh elements.
data-smart-animated attribute. When SmartMotion fires
smart-page-enter with the new container, SmartEffects removes all those flags
so the elements animate again on the fresh DOM — giving each page its own entrance animation
without any per-page configuration.
Built-in Presets
Use type="preset-name" to run a preset. Each preset is a function that receives
the current scope (Barba container or document) and returns an Anime.js config object.
.card elements pop in with a staggered translateY + opacity from..fade-up-item or a custom target..modal-dialog from 0.88 with easeOutBack bounce..sidebar in from -300px on the X axis.table tr rows in with a slight left-slide and fade..is-invalid / .form-error elements horizontally.<!-- Run a preset once on page load --> <smart-effects type="card-stagger"></smart-effects> <!-- Run preset on scroll (fires when elements enter viewport) --> <smart-effects type="table-stagger" trigger="scroll"></smart-effects> <!-- Preset with a custom target selector override --> <smart-effects type="fade-up" target=".my-items"></smart-effects> // From JS — call a preset programmatically: const fx = document.querySelector('smart-effects'); fx.playPreset('card-stagger'); fx.playPreset('error-shake', '.my-form .is-invalid'); // with target override
Trigger Modes
Control when the animation fires using the trigger attribute.
| trigger | Behaviour | Use case |
|---|---|---|
| page | Fires once on initial page load and again on every Barba navigation. Default. | Hero sections, card grids, page-level entrance animations. |
| scroll | Uses IntersectionObserver. Elements are pre-hidden (opacity:0) and animate in when they enter the viewport. Cleaned up correctly on navigation. |
Long pages with sections below the fold. Timeline items, feature lists. |
| hover | Attaches mouseenter listeners to all matching target elements. Re-wired on every navigation. |
Card hover effects, icon bounce on hover. |
| click | Attaches click listeners to all matching target elements. Can use a preset or attribute-driven config. |
Button press feedback, toggle animations. |
| manual | Does nothing automatically. Call fx.play() or fx.playPreset() from your own JS. |
Animations triggered by form submission, AJAX response, or custom logic. |
<!-- Page trigger (default) --> <smart-effects type="card-stagger"></smart-effects> <!-- Scroll trigger — elements animate when they scroll into view --> <smart-effects trigger="scroll" target=".feature-item" translateY="40" opacity="0" duration="700" ></smart-effects> <!-- Hover trigger --> <smart-effects trigger="hover" target=".card" scale="0.97" duration="200" ></smart-effects> <!-- Click trigger with button-click preset --> <smart-effects trigger="click" type="button-click" target=".btn" ></smart-effects> <!-- Manual trigger — call from JS --> <smart-effects id="myFx" trigger="manual" type="card-stagger"></smart-effects> document.querySelector('smart-form').addEventListener('smart-form-success', () => { document.getElementById('myFx').play(); });
Custom Attribute-Driven Animations
When no type preset is set, SmartEffects builds an Anime.js config directly
from the element's attributes. Set any transform or opacity attribute and SmartEffects
animates from that value to its natural resting state (0 for transforms,
1 for opacity). If no attributes are set, it defaults to a simple fade-up.
<!-- Fade up from 40px below --> <smart-effects target=".hero" translateY="40" opacity="0" duration="900" easing="easeOutExpo" ></smart-effects> <!-- Slide in from left --> <smart-effects target=".sidebar-nav" translateX="-60" opacity="0" duration="500" ></smart-effects> <!-- Scale pop-in --> <smart-effects target=".badge" scale="0.5" opacity="0" duration="400" easing="easeOutBack" ></smart-effects> <!-- Rotation entrance --> <smart-effects target=".icon-spin" rotate="-45" opacity="0" duration="600" ></smart-effects> <!-- Initial delay --> <smart-effects target=".cta-section" translateY="24" opacity="0" delay="400" duration="700" ></smart-effects> // All attributes: translateX · translateY · scale · rotate · opacity // · delay · duration · easing (any Anime.js easing string)
Auto Mode
Add the auto boolean attribute for zero-config animations. SmartEffects scans
the page for standard element groups and applies appropriate presets automatically. It also
wires nav link click micro-interactions. This is the recommended mode for base.html —
it covers the most common cases without any per-page configuration.
<!-- Drop in base.html — animates everything automatically --> <smart-effects auto></smart-effects> // What auto mode targets automatically: // .card → card-stagger preset (translateY + opacity stagger) // table tr → table-stagger preset (translateX + opacity stagger) // .list-group-item → translateX fade (custom, no preset needed) // .sidebar → sidebar-slide preset (slides in from left) // .nav-link → scale click effect (micro-interaction on click)
You can combine auto with a specific type or target
— auto mode runs alongside, not instead of, any other configuration:
<!-- Auto mode + explicit hero animation --> <smart-effects auto target=".hero-title" translateY="60" opacity="0" duration="1000"></smart-effects> <!-- Auto mode + scroll preset for below-fold sections --> <smart-effects auto></smart-effects> <smart-effects trigger="scroll" target=".section-block" translateY="30" opacity="0"></smart-effects> <!-- Multiple smart-effects tags are allowed — each runs independently -->
SmartEffects Reference
| Attribute | Type | Description | Default |
|---|---|---|---|
| auto | boolean | Enable zero-config auto mode. Scans for .card, table tr, .list-group-item, .sidebar, and .nav-link elements. |
false |
| type | string | Name of a built-in preset: card-stagger · fade-up · modal-pop · sidebar-slide · table-stagger · button-click · input-focus · error-shake. |
— |
| target | string | CSS selector for elements to animate. Required when using custom attribute animation. Used as override target for presets that support it. | — |
| trigger | string | page · scroll · hover · click · manual. When to fire the animation. |
page |
| delay | number | Initial delay before the animation starts (ms). For staggered presets, this becomes the stagger start offset. | 0 |
| duration | number | Animation duration in ms. | 700 |
| easing | string | Any valid Anime.js easing string: easeOutCubic, easeOutExpo, easeOutBack, spring(1,80,10,0), etc. |
easeOutCubic |
| translateX | number | Animate from this X offset (px) → 0. Positive = from right, negative = from left. | — |
| translateY | number | Animate from this Y offset (px) → 0. Positive = from below, negative = from above. | — |
| scale | number | Animate from this scale → 1. Use values like 0.8 for scale-in, 1.2 for scale-down. |
— |
| rotate | number | Animate from this rotation (deg) → 0. | — |
| opacity | number | Animate from this opacity → 1. Use 0 for a fade-in. |
— |
Public JS API
| Method | Description |
|---|---|
| play(selector?) | Manually trigger the animation. Pass an optional selector to override target. Use when trigger="manual". |
| playPreset(name, targetOverride?) | Run a specific named preset programmatically. Optionally override the target selector. Safe to call at any time. |
Card Stagger on Page Load
The most common pattern — a grid of cards that stagger in when the page loads and re-animates on every Barba navigation. SmartEffects automatically skips already-animated cards and re-animates them fresh on the next page visit.
<!-- In base.html — fires on every page automatically --> <smart-effects auto></smart-effects> <!-- Or explicitly on a specific page --> <smart-effects type="card-stagger"></smart-effects> <!-- Your Django template --> <div class="row"> < for item in items > <div class="col-md-4"> <div class="card"> <div class="card-body"> <h5></h5> </div> </div> </div> < endfor > </div>
Scroll-Triggered Sections
Use trigger="scroll" for content below the fold. SmartEffects pre-hides the
targets at opacity:0 and triggers individual animations via
IntersectionObserver as each element enters the viewport. On Barba navigation,
all scroll observers are automatically disconnected and recreated for the new page.
<!-- One tag handles all .feature-item elements on the page --> <smart-effects trigger="scroll" target=".feature-item" translateY="40" opacity="0" duration="700" easing="easeOutExpo" ></smart-effects> <!-- Or combine with a preset --> <smart-effects trigger="scroll" type="fade-up" target=".section-block" ></smart-effects> // IntersectionObserver config used internally: // threshold: 0.12 (fires when 12% visible) // rootMargin: '0px 0px -40px 0px' (40px buffer from bottom)
Manual Trigger & JS API
Trigger animations from your own JavaScript — useful after form submissions, AJAX responses, SmartTable row loads, or any dynamic content insertion.
<!-- Declare with trigger="manual" --> <smart-effects id="formFx" trigger="manual" type="card-stagger" ></smart-effects> const fx = document.getElementById('formFx'); // After SmartForm success — animate new table rows document.querySelector('smart-form').addEventListener( 'smart-form-success', () => { fx.playPreset('table-stagger'); } ); // After inline validation — shake invalid fields document.querySelector('smart-form').addEventListener( 'smart-form-error', () => { fx.playPreset('error-shake', '.is-invalid'); } ); // Custom selector at call time — override target dynamically fx.play('.newly-added-items'); // Works with smart-page-enter too — runs after every Barba nav window.addEventListener('smart-page-enter', () => { fx.play(); });
Django + Barba Page Setup
Each Django template that uses Barba transitions needs only two things: extend
base.html and set a namespace block. Page-specific scripts go
inside so SmartMotion can re-execute them on every
navigation. The is for styles only — SmartMotion
extracts and re-injects those automatically.
< extends 'base.html' > < load static >Dashboard dashboard <!-- Page-specific styles (SmartMotion re-injects on navigation) --> <style> .stat-card { border-radius: 12px; } </style> < endblock > <div class="container py-4"> <div class="row g-3"> < for stat in stats > <div class="col-md-3"> <div class="card stat-card"></div> </div> < endfor > </div> </div> <!-- Page scripts go here — SmartMotion re-executes on every navigation. Use DOMContentLoaded OR just run directly (SmartMotion fires DOMContentLoaded too). --> <script> // This runs on first page load AND on every Barba navigation to this page document.addEventListener('DOMContentLoaded', () => { initDashboardCharts(); }); </script> < endblock > # views.py — nothing special needed def dashboard(request): return render(request, 'dashboard.html', { 'stats': get_stats() })
< block content >
are wrapped in an IIFE by SmartMotion before execution, so variable naming conflicts between
navigations are avoided. Use window.barbaCleanup(['myVar', 'myChart']) in a
smart-page-leave listener to clean up any globals from the current page before
the next one loads.