We Need to Talk About Website Animations: GSAP, CSS-Only Motion, and Where Barba.js Fits In
—Most websites don’t fail because they have no animation.
They fail because they have the same animation everywhere.
You’ve seen it. Every section fades in. Every card fades in. Every image fades in.
Technically correct. Strategically weak.
At A Digital, we still use fade-ins. But we don’t use them blindly. Motion should support hierarchy, clarity, and pacing, not distract from content.
And this isn’t theoretical for us. We’ve baked this approach into our boilerplate so starter projects can launch faster with better defaults.
Why This Matters in Our Boilerplate
Our boilerplate is built for real starter projects.
Not throwaway demos. Not over-engineered monsters. Real websites that need to ship fast and still feel premium.
So we added a reusable animation layer that gives teams structure from day one:
- data-reveal and data-reveal-group for controlled entrance animation
- Scroll-triggered reveals that can run once
- Count-up patterns for key metrics
- Parallax hooks where depth adds value
- Section underline accents for subtle hierarchy
- prefers-reduced-motion support for accessibility
The big win is consistency.
You’re not rebuilding animation logic every sprint. You start from a system that already behaves properly.
GSAP: Best for Timing, Scroll Logic, and Real Control
When animation depends on context, GSAP is usually the right tool.
In one of our current setups, we use a reusable reveal system based on data- attributes (data-reveal, data-reveal-group, data-count, and similar patterns). That means motion can be controlled per section without writing fresh animation code every time.
A simplified reveal example from that pattern:
gsap.from(item, { y: 24, autoAlpha: 0, duration: 0.6, ease: "power2.out", scrollTrigger: { trigger: item, start: "top bottom", once: true } });
And for parallax sections:
Need help with your digital strategy?
Get in touch — we'd love to hear about your project.
gsap.to(el, { y: el.offsetHeight * -0.5, ease: "none", scrollTrigger: { trigger: el, start: "top bottom", end: "bottom top", scrub: 1 } });
What this gives us:
- Consistent defaults with flexible overrides.
- Scroll-triggered reveals that can run once and stay done.
- Better handling of async layout shifts and media loading.
- Safeguards for “stuck hidden” states.
- Built-in respect for prefers-reduced-motion.
So yes, elements fade in. But not as a copy-paste effect across the entire site.
CSS-Only Animation (No JS): Best for Lightweight UI Motion
Not every animation needs JavaScript.
For hover states, focus feedback, and simple transitions, CSS is cleaner and faster to maintain.
.reveal { opacity: 0; transform: translateY(16px); transition: opacity .5s ease, transform .5s ease; }
.reveal.is-visible { opacity: 1; transform: translateY(0); }
@media (prefers-reduced-motion: reduce) { .reveal { opacity: 1; transform: none; transition: none; } }
CSS-only is ideal for:
- Button and link transitions.
- Subtle icon movement.
- Small UI state changes.
- Basic keyframe loops and emphasis effects.
Benefits are simple: lower complexity, less JS overhead, easier maintenance.
The tradeoff is control. CSS can’t handle advanced scroll sequencing or dynamic runtime behavior as well as GSAP can.
Barba.js: Great for Page Transitions, But Use with Intent
Barba.js can create smooth page-to-page transitions in multi-page websites.
Done well, it gives a polished, app-like feel without a full SPA rebuild.
A minimal GSAP + Barba transition setup looks like this:
barba.init({ transitions: [{ name: "fade", leave() { return gsap.to("[data-barba='container']", { autoAlpha: 0, duration: 0.25 }); }, enter() { return gsap.from("[data-barba='container']", { autoAlpha: 0, duration: 0.25 }); } }] });
But it’s not free value.
You need:
- Clear transition design logic.
- Script lifecycle management across route changes.
- Reliable reinitialization for animation modules after enter/leave events.
In local codebases we reviewed, GSAP appears often, while Barba.js is less common. That usually reflects a practical choice: keep architecture simple unless cross-page transitions are core to the experience.
Page Speed: The Bit People Skip
Animations can hurt performance if they’re used badly.
That’s exactly why we’re deliberate.
In the boilerplate approach, we aim to protect page speed by:
- Animating transform and opacity wherever possible
- Avoiding expensive layout-triggering properties
- Running scroll reveals once when appropriate
- Using CSS for simple interactions instead of JS
- Limiting heavier motion like parallax to places it actually adds value
- Respecting reduced-motion preferences
The goal is not “more animation.”
The goal is motion that feels intentional without dragging down Core Web Vitals.
Why This Is Good for Starter Projects
Starter projects need momentum.
Our boilerplate gives teams a reliable motion system early, so they can move quickly without creating animation debt.
You get:
- Better defaults
- Faster delivery
- More consistent UX
- Fewer regressions later
The Practical Rule
Use the lightest tool that solves the actual UX problem.
- Use CSS for simple UI motion.
- Use GSAP for scroll logic, sequencing, and dynamic behavior.
- Use Barba.js when cross-page transition UX is a genuine requirement.
Animation should improve comprehension and flow.
If everything fades in the same way, nothing feels important.
If your current site relies on generic fade-ins everywhere, keep the motion, but make it intentional. That’s usually where quality jumps fast.
