Why I Stopped Using Animation Libraries (And Started Again)

July 25, 2024

There's this peculiar relationship developers have with animation - it oscillates between reverence and revulsion, often within the same project. I found myself squarely in the revulsion camp circa 2019, though not through any philosophical stance but rather through bitter experience.

The catalyst was a client project that began innocuously enough. I'd crafted what I considered an elegant, restrained interface - clean typography, purposeful whitespace, interactions that felt inevitable rather than imposed. Then came the feedback: "make it more dynamic." Those three words, accompanied by a reference site that seemed to have been designed by someone with a pathological fear of stillness.

Every element demanded attention through motion. Buttons didn't just respond to interaction; they pulsated with the desperate energy of a nightclub's neon signage. Text didn't simply appear; it careened onto the screen from trajectories that defied both logic and user expectation. The parallax background created a nauseating sense of perpetual motion that made me question whether I was viewing a website or experiencing some form of digital seasickness.

Three weeks of implementation transformed their site into what I can only describe as a digital carnival - all spectacle, no substance. The irony wasn't lost on me: users complained about usability while the client effused about "engagement." That disconnect taught me to distrust animation entirely, though I'd later learn I was conflating poor execution with inherent medium limitations.

The nadir of this project - and perhaps my entire relationship with motion design - manifested in what I've come to think of as "the bouncing button incident." The client wanted their call-to-action to "really pop," a phrase that should have triggered every alarm in my professional consciousness but somehow didn't.

The implementation was technically flawless:

@keyframes annoying-bounce {
  0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
  40% { transform: translateY(-10px); }
  60% { transform: translateY(-5px); }
}

.cta-button {
  animation: annoying-bounce 3s infinite;
}

What emerged was a masterclass in how technical competence can serve fundamentally misguided objectives. The button performed its choreographed routine with metronomic precision - a relentless three-second cycle that transformed what should have been a moment of user agency into an exercise in involuntary attention capture.

Users reported an inability to concentrate on the surrounding content. The button's perpetual motion created a kind of visual tinnitus - always present in peripheral vision, always demanding cognitive resources that should have been allocated to understanding the actual value proposition. I'd created not a call-to-action but a call-to-distraction.

This experience crystallized a realization that would shape my approach for years: most animation serves the designer's ego rather than the user's needs. It's visual masturbation masquerading as user experience enhancement.

What followed was a period I now recognize as necessary but ultimately reductive - two years of aesthetic monasticism where I eschewed motion entirely. Every interface became an exercise in digital brutalism: stark, functional, deliberately devoid of the flourishes I'd come to associate with user manipulation.

The results were undeniably effective within their constraints. Users navigated without distraction, pages achieved loading speeds that bordered on the instantaneous, and I received exactly zero complaints about motion-induced discomfort. The metrics supported this approach - engagement improved, task completion rates climbed, and bounce rates plummeted.

Yet something ineffable was absent from these interactions. The interfaces possessed all the warmth of a medical instrument - precise, reliable, but fundamentally alienating. State changes occurred with the abruptness of a light switch, offering no acknowledgment of user agency, no sense that the system recognized or responded to human input beyond mere functional compliance.

I began to suspect I'd overcorrected, trading one form of dysfunction for another. The absence of motion created its own kind of cognitive dissonance - interactions that felt less like conversations with a responsive system and more like commands issued to an indifferent machine.

Epiphany arrived through the mundane act of processing a payment via Stripe's dashboard - an interface that would fundamentally recalibrate my understanding of motion's potential. Here was animation deployed with surgical precision: buttons that acknowledged hover states through subtle luminosity shifts, form sections that expanded with the organic rhythm of breathing, loading indicators that suggested progress rather than demanding patience.

The sophistication lay not in the individual effects but in their collective restraint. Each motion served a communicative function - confirming user intent, indicating system state, or guiding attention toward consequential actions. More crucially, these animations operated below the threshold of conscious awareness until their absence would render the interface noticeably less responsive.

This realization forced a uncomfortable reckoning with my previous absolutism. I'd been conflating animation with gratuitous motion, failing to distinguish between decoration and communication. Stripe's interface demonstrated that motion could serve as a form of visual rhetoric - persuasive not through manipulation but through clarity of intent and respect for user cognition.

Rehabilitation required establishing a philosophical framework that could prevent regression into previous excesses while allowing for meaningful exploration of motion's communicative potential. The principles I developed weren't merely technical constraints but ethical guidelines for respectful user interaction:

Purpose became paramount - every motion needed to justify its existence through functional contribution rather than aesthetic appeal. This eliminated the vast majority of decorative animations that had characterized my earlier work.

User agency remained inviolate - the system could respond to human input but never initiate motion autonomously. This prevented the attention-hijacking behaviors that had made my previous animations so problematic.

Temporal constraints acknowledged human perception limitations - interface feedback needed to feel instantaneous (under 300ms) while transitions could afford slightly more duration without feeling sluggish.

Device diversity demanded testing beyond the high-performance development environment - animations that felt smooth on a MacBook Pro could become stuttering distractions on older mobile hardware.

These weren't arbitrary restrictions but distillations of hard-won wisdom about the intersection of human psychology and digital interaction.

Micro-Interactions That Actually Work

Started small with button feedback:

.button {
  background: #007bff;
  transition: all 0.15s ease;
  transform: translateY(0);
}

.button:hover {
  background: #0056b3;
  transform: translateY(-1px);
}

.button:active {
  transform: translateY(0);
  transition-duration: 0.05s;
}

The key insight: the animation confirms the user's action. Hover = "I can click this." Active = "I did click this." It's communication, not decoration.

Form Validation Animations

This one changed my mind about animations completely. Instead of just showing error text, I added a subtle shake:

@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-5px); }
  75% { transform: translateX(5px); }
}

.input-error {
  animation: shake 0.3s ease-in-out;
  border-color: #dc3545;
}
function validateForm(input) {
  if (!input.value) {
    input.classList.add('input-error');
    // Remove class after animation completes
    setTimeout(() => input.classList.remove('input-error'), 300);
    return false;
  }
  return true;
}

Users immediately understand something went wrong. No need to hunt for error messages. The shake says "nope, try again."

Loading States Done Right

Loading spinners are everywhere, but most are terrible. Here's what I learned works:

/* Bad - draws too much attention */
.spinner-bad {
  animation: spin 0.5s linear infinite;
}

/* Good - subtle and calming */
.spinner-good {
  animation: spin 2s linear infinite;
  opacity: 0.6;
}

@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

Slower is better for loading animations. Fast spinners make users anxious. Slow ones feel more controlled.

Skeleton screens work even better:

.skeleton {
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: loading 1.5s infinite;
}

@keyframes loading {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

Shows the shape of content that's coming. Users know what to expect.

The JavaScript Animation Rabbit Hole

CSS animations work great for simple stuff. But I wanted more control for complex interactions.

First attempt with vanilla JavaScript was... rough:

function slideIn(element) {
  let start = null;
  const duration = 300;
  
  function animate(timestamp) {
    if (!start) start = timestamp;
    const progress = (timestamp - start) / duration;
    
    if (progress < 1) {
      element.style.transform = `translateX(${-100 + (progress * 100)}%)`;
      requestAnimationFrame(animate);
    } else {
      element.style.transform = 'translateX(0)';
    }
  }
  
  requestAnimationFrame(animate);
}

This works, but it's a lot of code for a simple slide. And I had to write easing functions by hand. Not fun.

Discovering Framer Motion

When I started using React, someone recommended Framer Motion. I was skeptical - another animation library to learn?

But this blew my mind:

import { motion } from 'framer-motion';

function Card({ children }) {
  return (
    <motion.div
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      whileHover={{ scale: 1.02 }}
      transition={{ duration: 0.2 }}
    >
      {children}
    </motion.div>
  );
}

That's it. No keyframes, no manual state management, no complex JavaScript. Just describe what you want and it handles the rest.

Page transitions became trivial:

function PageTransition({ children }) {
  return (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
      transition={{ duration: 0.3 }}
    >
      {children}
    </motion.div>
  );
}

The Performance Reality Check

All these animations look great on my MacBook Pro. Then I tested on a 3-year-old Android phone and everything was janky.

The problem: I was animating the wrong properties.

/* Bad - causes layout recalculation */
.slide-bad {
  transition: left 0.3s ease;
}

/* Good - uses GPU acceleration */
.slide-good {
  transition: transform 0.3s ease;
}

Only animate these properties for smooth performance:

  • transform (translate, scale, rotate)
  • opacity
  • filter (with caution)

Everything else can cause layout thrashing on slower devices.

Accessibility: The Thing I Forgot

Built this beautiful slide-in navigation menu. Looked amazing. Then someone mentioned they get motion sickness from animations.

Felt terrible. Added this immediately:

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
// Check for reduced motion preference
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');

if (!prefersReducedMotion.matches) {
  // Only animate if user is okay with motion
  element.animate(keyframes, options);
}

Some users need animations disabled for medical reasons. Always respect that preference.

My Current Animation Philosophy

After years of trial and error, here's what I've learned:

Good animations:

  • Provide feedback for user actions
  • Guide attention to important changes
  • Make state transitions feel natural
  • Stay out of the way

Bad animations:

  • Animate for no reason
  • Draw attention away from content
  • Take longer than 500ms for UI interactions
  • Ignore user preferences

Tools I Actually Use Now

For simple stuff: CSS transitions and keyframes For React: Framer Motion (worth the bundle size) For complex timelines: GSAP (when I really need it) For performance testing: Chrome DevTools on throttled CPU

The Mistakes I Still Make

I'm not perfect. Still catch myself:

  • Making animations too slow (users are impatient)
  • Animating too many things at once (overwhelming)
  • Forgetting to test on mobile (always looks worse)
  • Adding animations because they look cool (not because they help)

Retrospective wisdom often feels simultaneously obvious and elusive - truths that seem self-evident in hindsight yet remained invisible during the struggle of discovery. The insights I wish I could transmit to my earlier self transcend mere technical knowledge, touching on fundamental questions of design philosophy and user empathy.

The primacy of restraint represents perhaps the most counterintuitive lesson. Beginning with stillness and adding motion only when it serves a demonstrable purpose inverts the typical design process, where animation often becomes an afterthought or, worse, a superficial enhancement applied to mask underlying usability problems.

Device diversity exposes the privilege inherent in development environments. The high-refresh displays and powerful processors that enable smooth animation during development bear little resemblance to the hardware constraints faced by many users. This disconnect can transform carefully crafted interactions into stuttering frustrations.

Accessibility considerations extend beyond mere compliance to encompass genuine inclusivity. Vestibular disorders affect a significant portion of the population, making motion-heavy interfaces not just uncomfortable but genuinely harmful. Respecting user preferences for reduced motion isn't a technical checkbox but an acknowledgment of diverse human needs.

The economics of attention suggest that animation's value lies not in quantity but in strategic deployment. A single, well-considered motion can communicate more effectively than a dozen competing effects, each diluting the others' impact.

Performance constraints aren't merely technical limitations but user experience fundamentals. Janky animations create cognitive friction that undermines their communicative purpose, transforming helpful feedback into frustrating distraction.

The Bottom Line

Animations aren't evil. Bad animations are evil.

Good animations make interfaces feel responsive and alive. They provide feedback, guide attention, and make interactions feel natural.

But they're seasoning, not the main course. Your content and functionality matter more than how prettily things slide around.

Start simple. Add animations that solve real problems. Test everything on slow devices. And remember - if users notice your animations, you're probably doing it wrong.

The highest achievement in motion design might be its own invisibility - animations so seamlessly integrated into the interaction paradigm that their presence enhances without announcing itself, creating interfaces that feel more responsive, more alive, more fundamentally human in their acknowledgment of user agency and intent.