CSS Animations vs Figma: Production Reality vs Design Prototypes
Figma's spring physics can't translate to CSS cubic-bezier—understand the gap between design and code
Designers export Figma prototypes with spring animations, elastic easing, and frame-by-frame transitions. You translate them to CSS, and everything feels wrong—springs become cubic-bezier curves, 120fps animations drop to 60fps, and that "bouncy" feel disappears. The core issue: Figma's animation engine simulates physics in a frame-independent timeline. CSS animations run on the browser's compositor with fixed easing functions. They're fundamentally different systems optimizing for different goals.
The Fundamental Mismatch
Figma animations are declarative time-based simulations. You specify duration, easing curves (including custom beziers), and keyframes. Figma renders these at whatever frame rate your machine supports (often 120fps on modern displays). When you play a prototype, Figma recalculates interpolated values for every frame.
CSS animations are browser-optimized state transitions. The browser converts your animation into compositor operations that can run on the GPU. Easing functions are limited to what the browser implements natively (cubic-bezier, steps, ease-in-out). Everything targets 60fps (or 120fps on high-refresh displays if the browser compositor supports it).
What Gets Lost in Translation
/* Figma: Spring animation (0.5s, stiffness: 300, damping: 20) */
/* Translates to CSS: ??? */
.figma-button {
transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* Problem: cubic-bezier can't replicate spring physics
- No overshoot control
- No stiffness/damping parameters
- Approximation feels "close but wrong"
*/Figma's Animation Model
Figma prototypes support:
- Smart Animate: Automatically interpolates between matching layers across frames (position, size, rotation, opacity, corner radius)
- Easing curves: Linear, Ease In/Out, custom cubic-bezier, and spring animations with stiffness/damping
- 120fps rendering: Smooth animations on high-refresh displays
- Frame-by-frame control: Designers can scrub through animation timelines to verify motion
CSS's Animation Model
CSS provides:
- Transitions: Interpolate between two states when a property changes
- Keyframe animations: Define multiple waypoints at percentages (0%, 50%, 100%)
- Compositor thread: transform and opacity run on GPU (no main thread blocking)
- Fixed easing: cubic-bezier, steps, ease/ease-in/ease-out/ease-in-out, linear
- 60fps target: Standard refresh rate (120fps possible on high-refresh displays if browser supports)
Advantages of CSS Animations
1. Performance: Compositor Thread Optimization
CSS animations for transform and opacity run on the compositor thread, independent of JavaScript execution. Even if the main thread is blocked (heavy script execution, layout thrashing), animations remain smooth.
/* Runs on compositor (GPU) */
.button {
transition: transform 0.3s ease-out, opacity 0.3s;
}
.button:hover {
transform: translateY(-4px);
opacity: 0.8;
}
/* Performance characteristics:
- No layout recalculation
- No main thread blocking
- Smooth 60fps even during JavaScript execution
- Hardware accelerated
*/Figma animations: Run in the Figma app (or browser for prototypes), not optimized for production web rendering. Every frame recalculates interpolated values, which would be expensive if implemented naively in JavaScript.
2. Zero Runtime Cost
CSS animations have no JavaScript overhead. The browser parses CSS once and hands animation control to the rendering pipeline.
<!-- No JavaScript needed -->
<button class="pulse-button">Click Me</button>
<style>
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.pulse-button:active {
animation: pulse 0.3s ease-in-out;
}
/* Advantages:
- No event listener overhead
- No requestAnimationFrame loop
- No JavaScript bundle size
- Works even if JS fails to load
*/
</style>Figma animations: Must be reimplemented in JavaScript (Framer Motion, GSAP, React Spring). Every animation adds to bundle size and runtime cost.
3. Respects User Preferences
CSS automatically respects prefers-reduced-motion, making your app accessible to users with vestibular disorders.
/* Normal animation */
.modal {
animation: slideIn 0.4s ease-out;
}
@keyframes slideIn {
from { transform: translateY(100px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
/* Disable for users who prefer reduced motion */
@media (prefers-reduced-motion: reduce) {
.modal {
animation: none; /* Instant appearance */
}
}
/* Figma prototypes: No built-in reduced motion support
Requires manual JavaScript implementation
*/4. Declarative & Maintainable
CSS animations are declarative: you specify what should happen, not how. No imperative animation loops or state management.
/* CSS: Declarative */
.notification {
animation: fadeOut 2s forwards;
animation-delay: 3s; /* Show for 3s, then fade */
}
@keyframes fadeOut {
to { opacity: 0; }
}
/* JavaScript equivalent: Imperative (more code) */
setTimeout(() => {
let opacity = 1;
const interval = setInterval(() => {
opacity -= 0.05;
notification.style.opacity = opacity;
if (opacity <= 0) clearInterval(interval);
}, 100);
}, 3000);
/* CSS wins: shorter, more readable, performant */Advantages of Figma Animations
1. Spring Physics & Natural Motion
Figma supports true spring animations with stiffness and damping parameters. CSS only has cubic-bezier curves, which can approximate springs but never replicate the physics.
/* Figma: Spring (stiffness: 300, damping: 20)
Produces natural overshoot and settle behavior
*/
/* CSS approximation: */
.button {
transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* Issues:
- Overshoot is fixed (can't adjust dynamically)
- No damping parameter (can't control oscillation)
- Feels "mechanical" compared to real physics
*/Solution (if springs are critical): Use JavaScript animation libraries like React Spring or Framer Motion that implement real spring physics.
2. Smart Animate: Automatic Matching
Figma's Smart Animate automatically interpolates between layers with the same name across frames. You rename a button from "Button" to "Button" across two screens, and Figma animates its position, size, color, and corner radius automatically.
CSS equivalent: Manual keyframe definition for every property change.
/* Figma Smart Animate: Automatic
Frame 1: Button at x=50, y=100, width=200px, red
Frame 2: Button at x=300, y=150, width=250px, blue
→ Figma animates position, size, and color automatically
*/
/* CSS: Manual keyframes for each property */
@keyframes buttonMove {
from {
transform: translate(50px, 100px) scale(1);
background: red;
}
to {
transform: translate(300px, 150px) scale(1.25);
background: blue;
}
}
/* More verbose, but also more explicit (easier to debug) */3. Designer-Controlled Iteration
Designers can preview animations instantly in Figma. Adjust timing, easing, and keyframes in real-time without developer involvement. CSS requires developer implementation for every design change.
Workflow comparison:
- Figma: Designer tweaks animation → Preview instantly → Ship prototype link for stakeholder review
- CSS: Designer specifies animation → Developer implements → Designer reviews build → Feedback loop → Developer adjusts → Repeat
Figma is faster for iteration. CSS is faster for production.
4. Frame-by-Frame Control
Figma lets designers scrub through animation timelines to verify motion at specific frames. CSS animations are runtime-only—you can't inspect intermediate states without DevTools.
Disadvantages of CSS Animations
1. Limited Easing Functions
CSS only supports cubic-bezier and steps easing. No springs, no elastic curves, no bounce (without custom keyframes).
/* CSS easing options: */
transition-timing-function: ease; /* cubic-bezier(0.25, 0.1, 0.25, 1) */
transition-timing-function: ease-in; /* cubic-bezier(0.42, 0, 1, 1) */
transition-timing-function: ease-out; /* cubic-bezier(0, 0, 0.58, 1) */
transition-timing-function: ease-in-out; /* cubic-bezier(0.42, 0, 0.58, 1) */
transition-timing-function: linear; /* No easing */
transition-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); /* Custom */
/* Missing: spring(), elastic(), bounce() (available in JS animation libs) */2. No Animation Chaining (Without JavaScript)
CSS can't conditionally chain animations based on runtime state. JavaScript is required for complex choreography.
/* Can't do in CSS alone:
1. Animate modal sliding in
2. Wait for modal to settle
3. Animate modal content fading in
4. On close: reverse animations in sequence
*/
/* Requires JavaScript: */
async function showModal(modal) {
const slideIn = modal.animate([
{ transform: 'translateY(100px)', opacity: 0 },
{ transform: 'translateY(0)', opacity: 1 }
], { duration: 300, fill: 'forwards' });
await slideIn.finished;
const content = modal.querySelector('.content');
content.animate([
{ opacity: 0 },
{ opacity: 1 }
], { duration: 200, fill: 'forwards' });
}
/* CSS animations: Good for simple transitions
JavaScript + Web Animations API: Required for choreography
*/3. Can't Animate Discrete Properties
CSS can't smoothly interpolate discrete properties like display, grid-template-columns, or visibility (see related insight on CSS Grid animations). These properties change instantly, not gradually.
/* Can't animate: */
.element {
display: none;
transition: display 0.3s; /* Doesn't work! */
}
.element.show {
display: block; /* Instant change, no transition */
}
/* Workaround: Animate opacity/transform instead */
.element {
opacity: 0;
transform: scale(0.95);
transition: opacity 0.3s, transform 0.3s;
}
.element.show {
opacity: 1;
transform: scale(1);
}Disadvantages of Figma Animations
1. Not Production-Ready
Figma prototypes are not shippable code. Every animation must be manually reimplemented by developers. Figma-to-code tools (Anima, Locofy) can export HTML/CSS, but animations rarely translate correctly.
Reality: Figma prototypes are specs, not implementations. Budget time for developer translation.
2. No Responsive Behavior
Figma animations don't adapt to viewport size or device capabilities. A 1920×1080 prototype animation breaks on mobile unless manually adjusted.
CSS animations adapt via media queries:
/* Desktop: Full animation */
.card {
transition: transform 0.4s ease-out;
}
.card:hover {
transform: translateY(-20px) scale(1.05);
}
/* Mobile: Simplified animation (performance) */
@media (max-width: 768px) {
.card {
transition: transform 0.2s ease-out;
}
.card:hover {
transform: translateY(-8px); /* Less dramatic */
}
}
/* Figma: Requires separate mobile prototype */3. Bundle Size Cost (If Using Libraries)
To replicate Figma's spring animations in production, you need JavaScript animation libraries:
- Framer Motion: ~35KB gzipped
- React Spring: ~20KB gzipped
- GSAP: ~40KB gzipped (core + plugins)
CSS animations: 0KB (built into browser).
4. Accessibility Requires Extra Work
Figma has no prefers-reduced-motion support. Developers must implement accessibility manually when translating animations to code.
The Hybrid Approach: Best of Both Worlds
Use Figma for design exploration and stakeholder communication. Use CSS for production implementation. Bridge the gap with JavaScript animation libraries only when necessary.
Decision Matrix
/* Simple state transitions (80% of animations) */
→ Use CSS transitions
→ Example: Button hover, modal fade in/out, loading spinners
/* Spring physics or elastic motion (10% of animations) */
→ Use Framer Motion or React Spring
→ Example: Drawer slide-in with bounce, drag-and-release momentum
/* Complex choreography (8% of animations) */
→ Use Web Animations API or GSAP
→ Example: Multi-step onboarding sequences, parallax scrolling
/* Layout animations (FLIP technique) (2% of animations) */
→ Use JavaScript + transform animations
→ Example: List reordering, grid item expansionCommunication Protocol: Design Handoff
When designers export Figma prototypes for developer implementation:
- Document timing: List duration and easing for each animation (e.g., "Modal slide-in: 300ms, ease-out")
- Flag spring animations: Explicitly note which animations use spring physics (these require JS libraries)
- Provide cubic-bezier values: For custom easing, export cubic-bezier coordinates (Figma displays these when using custom curves)
- Annotate accessibility: Specify which animations should disable for
prefers-reduced-motion - Prioritize: Mark animations as "must-have" vs "nice-to-have" (helps developers allocate time)
Production Checklist
- Default to CSS: Use
transitionfor 80% of animations (hover, focus, state changes). - Animate only transform/opacity: Compositor-safe properties for smooth 60fps.
- Use JavaScript for springs: Install Framer Motion or React Spring only if spring physics are critical to UX.
- Respect prefers-reduced-motion: Always provide reduced motion alternatives.
- Test on low-end devices: Animations that feel smooth on MacBook Pro may jank on Android mid-range phones.
- Profile with DevTools: Use Chrome Performance tab to verify animations don't trigger layout/paint.
- Document easing curves: Keep a design system with approved cubic-bezier values (e.g.,
ease-out: cubic-bezier(0, 0, 0.2, 1)). - Use Figma for iteration, CSS for production: Designers prototype in Figma. Developers ship with CSS. Align on timing/easing during handoff.
Figma prototypes showcase ideal animations with spring physics and frame-perfect timing. CSS animations ship production-ready performance with zero JavaScript overhead. The gap between them is real—but understanding each tool's strengths makes the translation smoother. Use Figma to explore "what should this feel like?" Use CSS to answer "how do we ship this fast?"
Advertisement
Explore these curated resources to deepen your understanding
Official Documentation
MDN: CSS Animations
Complete reference for CSS animation properties and keyframes
MDN: transition-timing-function
Easing functions available in CSS (cubic-bezier, steps, linear)
MDN: prefers-reduced-motion
Media query for respecting user motion preferences
Web Animations API
JavaScript API for creating compositor-optimized animations
Tools & Utilities
Further Reading
Framer Motion - React Animation Library
Production-ready spring physics animations for React
React Spring - Spring Physics for React
Physics-based animations with spring dynamics (lighter than Framer)
Josh Comeau - Creating Usable and Accessible Motion
Best practices for accessible animations and reduced motion
Designing With Reduced Motion For Motion Sensitivities
Deep dive into accessibility considerations for animations (2020)
Related Insights
Explore related edge cases and patterns
Advertisement