Wunderlandmedia

Every JavaScript Animation Library You Can Replace With 3 Lines of CSS

Most JavaScript animation libraries solve problems that CSS solved natively in 2025. Here's what to drop and the 3 lines of CSS that replace it.

Kemal EsensoyModified on April 28, 2026
Every JavaScript Animation Library You Can Replace With 3 Lines of CSS
Insights & Ideas

I'll be honest. Last month I caught myself installing GSAP on a client project just to fade in some cards on scroll. Thirty kilobytes of JavaScript. To make a div go from invisible to visible.

That's when I stopped and asked myself: do I actually need this, or am I just doing what I've always done?

Turns out, most of the animations I've been reaching for JavaScript libraries to handle are now native CSS. Not "coming soon." Not "behind a flag." Shipping in browsers right now. And the performance difference isn't subtle.

You're Shipping 30KB of JavaScript to Fade In a Div

Animation libraries are the junk drawer of web development. You pull one in for a single fade-in effect, and suddenly every page on your site is loading 30 to 60 kilobytes of JavaScript that the browser has to download, parse, compile, and execute before anything moves.

Developer desk showing a bloated JavaScript bundle size chart with tangled dependency cables

I've done this more times than I'd like to admit. A client wants scroll animations, I reach for AOS because it's what I know. Another client wants page transitions, I install Framer Motion because the docs are nice. Never once did I stop to check if the browser could just do it.

That habit cost my clients real performance. Every byte of JavaScript costs more than a byte of CSS because JS has to be parsed and executed on the main thread. CSS animations run on the compositor thread and get GPU acceleration for free.

The Library Tax Nobody Talks About

Let me put some numbers on this. Here's what you're actually shipping when you install these libraries:

GSAP core comes in at roughly 23KB gzipped. Add ScrollTrigger and you're at 41KB. Framer Motion (now called Motion) is about 32KB gzipped. AOS is 14KB. Animate.css is 18KB. And if you're using Lottie-web, you're looking at 80KB or more.

These aren't small numbers. Motion alone gets 3.6 million weekly downloads. GSAP gets 1.47 million. That's a lot of projects shipping a lot of JavaScript for animations that CSS can handle natively.

The impact on Core Web Vitals is real. JavaScript animations block the main thread, which directly hurts your Interaction to Next Paint (INP) and Largest Contentful Paint (LCP) scores. CSS animations don't have this problem because they bypass the main thread entirely.

Fade In on Scroll? That's 3 Lines of CSS Now

This is the big one. The reason most people install AOS or GSAP ScrollTrigger is scroll-triggered animations. Elements that fade in, slide up, or scale when they enter the viewport.

Clean browser window showing smooth CSS scroll animation with floating code

Here's the old way with AOS: you import the library, add data-aos="fade-up" attributes to your HTML, call AOS.init() in your JavaScript, and link the AOS stylesheet. Four steps, two files, one dependency.

Here's the new way:

@keyframes fade-in {
  from { opacity: 0; translate: 0 20px; }
  to { opacity: 1; translate: 0; }
}

.animate-on-scroll {
  animation: fade-in linear both;
  animation-timeline: view();
  animation-range: entry 0% entry 100%;
}

Three properties. No JavaScript. No dependencies. No event listeners. The browser handles everything, and it runs on the compositor thread so it's butter smooth even on slower devices.

I wrote about a similar philosophy in my post about replacing WordPress plugins with native code. Same principle applies here: know your platform, and you need fewer abstractions.

Browser support: Chrome 115+, Safari 18+. Firefox is still behind a flag, but you can use @supports (animation-timeline: view()) for progressive enhancement. The animation just won't play in unsupported browsers, which is a perfectly acceptable fallback.

Scroll-Linked Progress Bars Without a Single Event Listener

Reading progress bars used to require scroll event listeners, requestAnimationFrame loops, and careful throttling to avoid jank. With GSAP ScrollTrigger, it was cleaner but still JavaScript.

Now it's this:

.progress-bar {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 3px;
  background: var(--accent);
  transform-origin: left;
  animation: grow-bar linear;
  animation-timeline: scroll();
}

@keyframes grow-bar {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

No JavaScript at all. The animation-timeline: scroll() ties the animation directly to the scroll position of the page. As the user scrolls, the bar grows. The browser does all the math.

Animating Elements Into View From display:none

This one is a genuine game changer. If you've ever used React Transition Group or Framer Motion's AnimatePresence, you know the pain of animating elements that go from display: none to display: block. CSS couldn't do it. That's why animation libraries existed in the React ecosystem.

@starting-style fixes this. It lets you define the "before" state for an element's first appearance:

dialog[open] {
  opacity: 1;
  scale: 1;
  transition: opacity 0.3s, scale 0.3s, display 0.3s allow-discrete;
}

@starting-style {
  dialog[open] {
    opacity: 0;
    scale: 0.95;
  }
}

This works for dialogs, popovers, any element toggling display. Chrome 117+, Safari 17.5+, Firefox 129+. That's cross-browser baseline in 2026.

This was the reason people reached for animation libraries in React. Not anymore.

Page Transitions Without React Router + Framer Motion

The View Transitions API is probably the most impressive CSS feature I've seen in years. Same-document transitions work in Chrome 111+, Safari 18+, and Firefox 133+. Cross-document transitions for multi-page apps landed in Chrome 126+.

The old approach: FLIP calculations, position tracking, React state management, hundreds of lines of code. The new approach:

::view-transition-old(root) {
  animation: fade-out 0.2s ease;
}

::view-transition-new(root) {
  animation: fade-in 0.2s ease;
}

For multi-page apps, you literally just add a CSS rule and the browser handles cross-page morphing. No JavaScript framework required. This contributes directly to that professional web design feel that clients notice but can't articulate.

Animating Height From 0 to Auto (Yes, Finally)

Every web developer has tried to animate height: auto at some point. Every web developer has failed. This has been an unsolvable CSS problem for over a decade.

Side-by-side comparison of heavy JavaScript and lightweight CSS approaches

interpolate-size: allow-keywords solves it:

:root {
  interpolate-size: allow-keywords;
}

details[open] > .content {
  height: auto;
  transition: height 0.3s ease;
}

Smooth accordion animations, <details> elements that open gracefully, collapsible panels that don't jump. One line in your CSS reset and it just works.

Caveat: this is Chromium only right now (Chrome 129+, Edge 129+). Firefox and Safari don't support it yet. But you can add it as progressive enhancement. In unsupported browsers, the height change is instant, which is the same behavior everyone has right now anyway.

When You Should Still Use a Library

I'm not going to pretend CSS replaces everything. It doesn't. Here's where you should keep your JavaScript animation library:

Complex sequenced timelines. If you need 15 elements animating in a specific choreographed order with precise timing, GSAP's timeline API is still unmatched. CSS can chain animations, but it gets messy fast.

Physics-based spring animations. Framer Motion's spring physics produce a feel that CSS easing curves can't replicate. If your design depends on that bouncy, natural motion, keep the library.

SVG path morphing. Lottie and GSAP's MorphSVG plugin handle complex vector animations that CSS simply can't touch.

Canvas and WebGL. If you're doing anything with <canvas>, you're already in JavaScript territory.

Here's my rule of thumb: if you can describe the animation in one sentence ("fade in on scroll," "slide up when visible," "progress bar on scroll"), CSS can do it. If you need a paragraph to explain it, keep the library.

Good user experience design means using the right tool for the job, not the most powerful tool for every job.

The Migration Checklist

If you want to start replacing JavaScript animation libraries with CSS, here's what I'd do:

1. Search your codebase. Look for gsap.to, gsap.from, data-aos, motion.div, animate__. List every animation you're using.

2. Categorize each one. Can you describe it in one sentence? If yes, it's a CSS candidate. If no, flag it as "keep."

3. Replace in order of impact. Start with scroll-triggered animations (biggest bundle savings), then entry animations (@starting-style), then page transitions.

4. Use @supports for progressive enhancement. Wrap CSS-only animations in @supports blocks so unsupported browsers just don't animate rather than breaking.

5. Test in Safari and Firefox, not just Chrome. Chrome is ahead on some of these features. Don't ship something that only works in Chromium.

6. Measure before and after. Run Lighthouse before you start and after you're done. The bundle size reduction alone should be obvious, but the INP improvement is what really matters.

And while you're cleaning up dependencies, remember that fewer npm packages also means a smaller attack surface. Every library you remove is one less supply chain risk.

The web platform has caught up to what animation libraries were doing five years ago. Some of it happened quietly, some of it is still landing, but enough of it is production-ready today that you should at least question every npm install that adds motion to a div.

I'm not saying throw away GSAP. I'm saying stop reaching for it by default. Check what CSS can do first. You might be surprised how often the answer is "everything you need."

If your site feels sluggish and you're not sure where to start optimizing, let's talk. Sometimes the biggest performance win is removing code, not adding it.

About the Author

KE

Kemal Esensoy

Kemal Esensoy, founder of Wunderlandmedia, started his journey as a freelance web developer and designer. He conducted web design courses with over 3,000 students. Today, he leads an award-winning full-stack agency specializing in web development, SEO, and digital marketing.

Replace JS Animation Libraries With CSS | Wunderlandmedia