--- 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'; {isOpen && ( Content )} ``` ### 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 (Web Platform Features)]] ## 🤖 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 (Web Platform Features)]] · [[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 |