# CSS Animations vs JavaScript Spring Libraries: When to Use Each for UI Interactions

> Learn when to use CSS animations for frequent UI interactions and JavaScript spring libraries for dynamic, physics-driven motion. Optimize your UI animations today.

- Repository: [Emil Kowalski/skills](https://github.com/emilkowalski/skills)
- Tags: comparison
- Published: 2026-08-05

---

**Prefer CSS animations for static, frequent UI interactions and reserve JavaScript spring libraries for dynamic, physics-driven motions where interruptibility matters.**

Modern web interfaces demand thoughtful animation choices. The `emilkowalski/skills` repository provides clear guidance on distinguishing between **pure CSS animations** and **JavaScript-driven spring libraries** based on motion predictability, performance constraints, and interaction patterns. This article breaks down the architectural factors that determine which tool belongs in your implementation.

## Motion Predictability: Static vs Dynamic

The fundamental divide lies in whether your animation's end state is known ahead of time.

### Pre-Defined Motion: CSS Wins

CSS animations excel at **pre-defined, static motion**—fades, slides, and simple transforms that never change at runtime. Because they execute on the compositor thread, they remain smooth even when the main thread handles heavy JavaScript or network operations. As noted in [`skills/emil-design-eng/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/emil-design-eng/SKILL.md), "CSS animations beat JS under load"【/skills/emil-design-eng/SKILL.md†L509-L513】.

This makes CSS ideal for:
- Button hover states
- Modal open/close sequences
- Dropdown reveals
- Toast notifications

### Dynamic, Physics-Based Motion: Springs Excel

JavaScript spring libraries like Framer Motion shine when the end state **cannot be predetermined**. Springs adapt their velocity and settle based on physical parameters, producing natural motion for drag interactions, gesture-driven toggles, and layout changes where dimensions are unknown【/skills/emil-design-eng/SKILL.md†L149-L155】.

The [`skills/pick-ui-library/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/pick-ui-library/SKILL.md) file explicitly recommends springs for "General-purpose animation (springs, layout animations, exit animations)"【/skills/pick-ui-library/SKILL.md†L34-L41】.

## Interruptibility and Retargeting Behavior

Not all animations handle interruption gracefully—this is where the two approaches diverge sharply.

### CSS Transitions vs Keyframes

**CSS transitions** can be retargeted mid-animation, smoothly moving from the current state to a new target. This property makes them suitable for rapidly toggled UI elements like toasts and switches【/skills/review-animations/STANDARDS.md†L79-L80】.

However, **CSS keyframe animations** restart from zero when interrupted. For UI elements toggled frequently, this creates jarring restarts rather than smooth reversals.

### Spring Velocity Preservation

Spring-based animations maintain **velocity when interrupted**, providing fluid reversal or direction changes. This is critical for gestures where users may change their mind mid-motion【/skills/emil-design-eng/SKILL.md†L195-L200】.

The `requestAnimationFrame`-driven nature of springs allows continuous recalculation based on changing inputs—impossible with declarative CSS.

## Performance and Threading Model

| Aspect | CSS Animations | JavaScript Springs |
|--------|---------------|-------------------|
| Execution thread | Compositor (off-main-thread) | Main thread |
| Frame drops under load | Rare | Possible (cited: Vercel dashboard issue)【/skills/emil-design-eng/SKILL.md†L507-L511】 |
| Best use case | High-frequency UI (>100×/day)【/skills/review-animations/STANDARDS.md†L9-L12】 | Occasional, complex interactions |

CSS animations do not compete with JavaScript, network, or paint work. For **keyboard shortcuts**, **command-palette toggles**, or any action performed frequently where lag feels sluggish, CSS is the safest choice【/skills/review-animations/STANDARDS.md†L9-L12】.

JavaScript springs risk frame drops during heavy load. Reserve them for interactions that cannot be expressed with CSS alone.

## Complexity and Maintainability Trade-offs

### CSS: Declarative Simplicity

CSS animations use straightforward syntax:

```css
.modal {
  opacity: 0;
  transform: translateY(-10px);
  transition: opacity 200ms ease-out, transform 200ms ease-out;
}

.modal.open {
  opacity: 1;
  transform: translateY(0);
}

```

This declarative approach keeps codebases small and fits the **standard animation budget of <300ms** recommended in [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md)【/skills/review-animations/STANDARDS.md†L49-L50】.

### JavaScript Springs: Imperative Power

Spring libraries require dependencies and imperative code:

```tsx
import { motion, useSpring } from "framer-motion";

function ExpandablePanel({ isOpen, children }) {
  const height = useSpring(0, { stiffness: 300, damping: 30 });
  
  // Dynamic height calculation required
  useEffect(() => {
    height.set(isOpen ? measuredHeight : 0);
  }, [isOpen, measuredHeight]);
  
  return <motion.div style={{ height }}>{children}</motion.div>;
}

```

This adds bundle size and cognitive overhead. Employ it only when the motion genuinely requires physical simulation.

## Accessibility: Reduced Motion Support

CSS provides native `prefers-reduced-motion` gating while preserving opacity and color transitions that aid comprehension【/skills/review-animations/STANDARDS.md†L37-L38】:

```css
@media (prefers-reduced-motion: reduce) {
  .modal {
    transition-duration: 0.01ms;
  }
}

```

JavaScript springs require **manual disable logic**, adding implementation burden to respect user preferences. Check this before committing to spring-based solutions.

## Practical Implementation Guidelines

1. **Default to CSS** for any animation that is static, inexpensive, and occurs frequently.

2. **Reserve JavaScript springs** for dynamic, interruptible, or physics-driven interactions where the final state depends on user input.

3. **Honor performance budgets**: keep UI motions under 300ms and avoid animation on actions performed more than 100 times daily.

4. **Verify reduced-motion handling**: CSS degrades gracefully; springs need explicit checks.

## CSS Animation Example: Retargetable Hover State

The following demonstrates CSS transitions that can be **retargeted instantly**, matching the repository's guidance:

```html
<button class="cta">Click me</button>

<style>
.cta {
  background: #0066ff;
  color: #fff;
  padding: 0.5rem 1rem;
  border: none;
  cursor: pointer;
  transition: background-color 150ms ease-out, transform 150ms ease-out;
}

.cta:hover {
  background: #0055dd;
  transform: scale(1.05);
}

@media (prefers-reduced-motion: reduce) {
  .cta {
    transition: none;
  }
}
</style>

```

This satisfies the retargeting requirement that "CSS transitions retarget from the current state mid-animation"【/skills/review-animations/STANDARDS.md†L79-L80】.

## JavaScript Spring Example: Draggable Card

For gesture-driven values requiring velocity preservation:

```tsx
import { motion } from "framer-motion";

export default function DraggableCard() {
  return (
    <motion.div
      drag
      dragConstraints={{ left: 0, right: 300, top: 0, bottom: 300 }}
      style={{
        width: 200,
        height: 120,
        background: "#fff",
        borderRadius: 8,
        boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
      }}
      transition={{ type: "spring", stiffness: 300, damping: 30 }}
    />
  );
}

```

The spring maintains **velocity when released**, smoothly settling to rest—behavior impossible to replicate with CSS alone for this interaction pattern.

## Key Source Files in emilkowalski/skills

| File | Purpose |
|------|---------|
| [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) | Animation budgets, duration limits, and CSS vs JS performance comparisons |
| [`skills/emil-design-eng/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/emil-design-eng/SKILL.md) | Core philosophy on animation timing, spring benefits, and reduced-motion |
| [`skills/pick-ui-library/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/pick-ui-library/SKILL.md) | Scenario mapping for choosing appropriate animation tools |
| [`skills/improve-animations/AUDIT.md`](https://github.com/emilkowalski/skills/blob/main/skills/improve-animations/AUDIT.md) | Checklist for identifying over- or under-animated elements |

## Summary

- **CSS animations** run on the compositor thread, handle retargeting gracefully, and suit static, high-frequency UI interactions under 300ms.
- **JavaScript spring libraries** execute on the main thread, preserve velocity through interruptions, and enable physics-based motion for dynamic, gesture-driven interfaces.
- Prioritize CSS by default; introduce springs only when motion requires runtime adaptation or physical simulation.
- Always implement `prefers-reduced-motion` checks—CSS handles this natively, while springs need explicit disabling logic.

## Frequently Asked Questions

### Can I combine CSS animations with JavaScript springs in the same project?

Yes. Many interfaces benefit from hybrid approaches: CSS for standard hovers and reveals, springs for draggable or expandable components. The `emilkowalski/skills` repository explicitly recommends this layered strategy rather than forcing all animation through a single tool.

### How do I decide if an interaction needs spring physics?

Ask whether the animation's end state depends on user input or runtime measurement. If the final position, size, or value is unknown until interaction occurs—like a drag release point or dynamically measured content height—springs provide necessary adaptability. Pre-defined states suit CSS.

### What are the specific performance risks of JavaScript springs?

Springs run via `requestAnimationFrame` on the main thread, competing with JavaScript execution, React renders, and network activity. The repository cites a Vercel dashboard case where springs dropped frames under load【/skills/emil-design-eng/SKILL.md†L507-L511】. Limit springs to non-critical, infrequent interactions.

### Does Framer Motion work with reduced-motion preferences?

Not automatically. You must manually detect `prefers-reduced-motion` and disable or simplify spring animations. CSS transitions degrade more gracefully with media query controls, making them preferable for accessibility-critical paths unless you implement explicit spring disabling logic.