---
id: wiki-2026-0508-css-animations
title: CSS Animations & Performance
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [CSS animation, transform, will-change, prefers-reduced-motion, micro-interaction, FLIP, animation performance, view transitions]
duplicate_of: none
source_trust_level: A
confidence_score: 0.93
verification_status: applied
tags: [css, animation, performance, frontend, ux, accessibility, transform, will-change, prefers-reduced-motion, view-transitions]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
  language: CSS / JS
  framework: Web Animations API / Framer Motion / GSAP
---

# CSS Animations

## 📌 한 줄 통찰
> **"매 60 FPS 의 transform + opacity"**. 매 layout property 의 animate = 매 jank. 매 GPU compositor layer 의 ride. 매 modern: 매 `prefers-reduced-motion` + 매 View Transitions API. 매 functional > decorative.

## 📖 핵심

### 매 rendering pipeline
1. **Style** → 매 CSS apply.
2. **Layout (Reflow)** → 매 width / height / position 변경.
3. **Paint (Repaint)** → 매 color / shadow.
4. **Composite** → 매 GPU layer.

→ 매 transform + opacity 만 의 composite (skip layout, paint).

### 매 cheap properties (60 FPS)
- ✅ `transform`: translate, scale, rotate.
- ✅ `opacity`.
- ✅ `filter` (some).

### 매 expensive
- ❌ `width`, `height`, `padding`, `margin`.
- ❌ `top`, `left` (use translate instead).
- ❌ `box-shadow`, `border-radius` (some).
- ❌ background image animation.

### 매 핵심 technique

#### `transform: translate3d(0,0,0)` / `translateZ(0)`
- 매 GPU compositor layer 의 force.
- 매 will-change 의 modern alternative.

#### `will-change: transform`
- 매 hint 의 browser optimize.
- 매 overuse 의 memory waste.
- 매 use sparingly.

#### `position: absolute / fixed`
- 매 reflow 의 isolate.
- 매 sibling 의 영향 X.

#### CSS containment
```css
contain: layout paint;  /* 매 reflow 의 contain */
```

#### `prefers-reduced-motion`
```css
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; }
}
```

### 매 modern primitive

#### View Transitions API (Chrome 111+)
- 매 same / cross-document transition.
- 매 SPA 의 native.
- 매 baseline 2025 시작.

#### CSS @starting-style
- 매 entering element 의 initial state.

#### Scroll-driven animation
- 매 `animation-timeline: scroll()`.
- 매 timeline 의 expand.

#### CSS @scope
- 매 scoped animation.

### 매 timing 의 best practice
- **Duration**: 200-500 ms (UI). 매 너무 길 → 매 wait. 매 너무 짧 → 매 invisible.
- **Easing**: `ease-out` (UI 의 보통), `ease-in-out` (smooth), `cubic-bezier` (custom).
- **Linear**: 매 mechanical (loading, progress).

### 매 functional purpose
1. **Feedback**: 매 click → 매 visual response.
2. **Continuity**: 매 state change 의 explain.
3. **Hierarchy**: 매 importance 의 emphasize.
4. **Spatial relation**: 매 from / to.
5. **Brand personality**.

### 매 anti-purpose
- 매 decoration only.
- 매 attention-seeking infinite loop.
- 매 long entrance animation.
- 매 intrusive auto-play.

### 매 accessibility
- 매 `prefers-reduced-motion`: 매 vestibular disorder.
- 매 `aria-busy`: 매 loading state.
- 매 focus-visible 의 keep.

### 매 animation library
- **Web Animations API** (native, modern).
- **Framer Motion** (React).
- **GSAP** (general, professional).
- **Motion One** (lightweight).
- **Lottie** (designer-friendly).
- **Auto-Animate** (FLIP automation).

## 💻 패턴

### Cheap animation (transform + opacity)
```css
.button {
  transition: transform 200ms ease-out, opacity 200ms ease-out;
}
.button:hover {
  transform: translateY(-2px) scale(1.02);
  opacity: 0.9;
}
```

### Avoid layout property
```css
/* ❌ Bad — 매 reflow */
.bad {
  transition: left 300ms;
}
.bad:hover { left: 100px; }

/* ✅ Good — 매 composite only */
.good {
  transition: transform 300ms;
}
.good:hover { transform: translateX(100px); }
```

### Reduced motion (accessibility)
```css
.fancy-animation {
  animation: bounce 2s ease infinite;
}

@media (prefers-reduced-motion: reduce) {
  .fancy-animation {
    animation: none;
  }
  
  /* 매 essential 의 keep, fancy 만 의 remove */
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
```

### View Transitions API
```css
/* Browser-side */
@view-transition {
  navigation: auto;  /* 매 same-document SPA */
}

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 250ms;
}
```

```js
// 매 imperative
async function navigate() {
  if (!document.startViewTransition) {
    updateDOM();
    return;
  }
  document.startViewTransition(() => updateDOM());
}
```

### Scroll-driven animation
```css
@keyframes fade-in {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: translateY(0); }
}

.section {
  animation: fade-in 1s linear;
  animation-timeline: view();
  animation-range: entry 0% cover 30%;
}
```

### Web Animations API (JS)
```js
const card = document.querySelector('.card');

const anim = card.animate(
  [{ transform: 'translateY(20px)', opacity: 0 },
   { transform: 'translateY(0)', opacity: 1 }],
  { duration: 300, easing: 'ease-out', fill: 'forwards' },
);

await anim.finished;
```

### FLIP technique (smooth list reorder)
```js
function flipReorder(items, mutate) {
  // 1. First — measure
  const first = new Map();
  items.forEach(el => first.set(el, el.getBoundingClientRect()));
  
  // 2. Last — apply mutation
  mutate();
  
  // 3. Invert + Play
  items.forEach(el => {
    const last = el.getBoundingClientRect();
    const dx = first.get(el).left - last.left;
    const dy = first.get(el).top - last.top;
    
    el.animate(
      [{ transform: `translate(${dx}px, ${dy}px)` }, { transform: 'none' }],
      { duration: 300, easing: 'cubic-bezier(0.2, 0, 0.2, 1)' },
    );
  });
}
```

### Performance: pause off-screen
```css
.spinner { animation: spin 2s linear infinite; }
.spinner.paused { animation-play-state: paused; }
```

```js
const observer = new IntersectionObserver(entries => {
  entries.forEach(entry => {
    entry.target.classList.toggle('paused', !entry.isIntersecting);
  });
});
document.querySelectorAll('.spinner').forEach(el => observer.observe(el));
```

### Framer Motion (React)
```jsx
import { motion, AnimatePresence } from 'framer-motion';

<AnimatePresence>
  {isOpen && (
    <motion.div
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      exit={{ opacity: 0, y: -20 }}
      transition={{ duration: 0.2, ease: 'easeOut' }}
    >
      Content
    </motion.div>
  )}
</AnimatePresence>
```

### Performance debugging (Chrome DevTools)
```
1. Chrome DevTools → Performance tab → Record.
2. Look for:
   - Long tasks (>50ms)
   - Layout shift count
   - Paint flashing (Rendering tab)
   - FPS drop
3. Layer borders enable: Rendering → Layer borders.
```

## 🤔 결정 기준
| 상황 | Approach |
|---|---|
| Hover effect | CSS transition (transform, opacity) |
| Modal entrance | CSS animation + scale + opacity |
| Page transition (SPA) | View Transitions API |
| List reorder | FLIP technique |
| Scroll animation | scroll-driven (modern) or IO + class |
| Designer-driven | Lottie (designer-friendly) |
| Complex sequence | GSAP / Framer Motion |
| Loading | CSS animation + accessibility |

**기본값**: CSS transition + transform + opacity. 매 prefers-reduced-motion. 매 functional only.

## 🔗 Graph
- 부모: [[Web-Performance]] · [[Frontend]]
- 변형: [[Transform]] · [[Will-Change]] · [[View-Transitions]]
- 응용: [[Web-Animations-API]] · [[Framer-Motion]] · [[GSAP]]
- Adjacent: [[Reflow-Repaint]] · [[GPU-Acceleration]] · [[Accessibility (A11y)|Accessibility]] · [[Baseline-Project]]

## 🤖 LLM 활용
**언제**: 매 frontend animation. 매 micro-interaction. 매 UX polish. 매 accessibility audit.
**언제 X**: 매 server-side. 매 non-visual.

## ❌ 안티패턴
- **`top` / `left` animate**: 매 reflow.
- **`width` / `height` animate**: 매 reflow.
- **No `prefers-reduced-motion`**: 매 accessibility violation.
- **`will-change` overuse**: 매 memory waste.
- **Long entrance** (>1 sec): 매 user wait.
- **Auto-play heavy animation**: 매 mobile data + battery.
- **Off-screen infinite loop**: 매 CPU 의 burn.

## 🧪 검증 / 중복
- Verified (web.dev animation, MDN, Paul Lewis 의 Aerotwist).
- 신뢰도 A.
- Related: [[Web-Performance]] · [[Accessibility (A11y)|Accessibility]] · [[Baseline-Project]] · [[FLIP-Technique]].

## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-04-26 | Auto-mapped |
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — pipeline + cheap/expensive + view-transitions + 매 CSS / FLIP / Framer code |
