CSS animations either run at silky 60fps or choppy slideshow speed — and the difference is which properties you animate. Animating width triggers layout recalculation on every frame. Animating transform bypasses layout entirely and runs on the GPU compositor. This guide explains the performance model and the patterns that keep animations smooth. This guide focuses on CSS animation performance, with the production trade-offs senior engineers need to make sound decisions.
⚡ TL;DR: Only animate: transform (translate, scale, rotate, skew) and opacity — they run on GPU compositor thread without touching layout. Everything else (width, height, margin, padding, top, left) triggers layout on every frame and causes jank. Use will-change sparingly for known animation targets.
The browser rendering pipeline — CSS animation performance
// Four stages: JavaScript → Style → Layout → Paint → Composite
//
// Composite only (GPU, 60fps guaranteed):
// - transform: translateX, translateY, scaleX, scaleY, rotate, skew
// - opacity
//
// Triggers Paint (slower, but ok for occasional changes):
// - color, background-color, border-color, box-shadow
//
// Triggers Layout (SLOW — recalculates entire page):
// - width, height, padding, margin, top, left, right, bottom
// - font-size, line-height, display, float, position
//
// Rule: animate ONLY transform and opacity for smooth 60fpsTransform vs position — the critical difference
/* WRONG: animating position properties causes layout thrashing */
@keyframes slideInBad {
from { left: -100px; } /* Triggers layout on every frame */
to { left: 0; }
}
/* RIGHT: animating transform — GPU compositor only */
@keyframes slideIn {
from { transform: translateX(-100px); } /* No layout, no paint, GPU only */
to { transform: translateX(0); }
}
.element {
animation: slideIn 0.3s ease-out;
}
/* Transitions — same principle */
/* WRONG: */
.btn { transition: width 0.3s; }
.btn:hover { width: 200px; } /* Layout on every frame */
/* RIGHT: */
.btn { transition: transform 0.3s; }
.btn:hover { transform: scaleX(1.1); } /* GPU only */will-change — hint GPU layer promotion
/* will-change: tell browser to promote element to GPU layer in advance */
.animated-card {
will-change: transform, opacity; /* GPU layer created before animation starts */
}
/* Apply only to elements you KNOW will animate */
/* NOT on every element — each GPU layer uses VRAM */
/* WRONG: */
* { will-change: transform; } /* Crashes GPU with thousands of layers */
/* Pattern: apply on hover/focus, remove when done */
.card:hover {
will-change: transform;
}
.card {
transition: transform 0.3s;
}
.card:hover {
transform: translateY(-8px);
}
.card:not(:hover) {
will-change: auto; /* Release GPU layer when not animating */
}Smooth keyframe animations
/* Entrance animation — fade + slide */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.card {
animation: fadeInUp 0.4s ease-out both;
}
/* Stagger: delay each child */
.card:nth-child(2) { animation-delay: 0.1s; }
.card:nth-child(3) { animation-delay: 0.2s; }
/* Respect prefers-reduced-motion */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}- ✅ Animate ONLY transform and opacity for 60fps
- ✅ will-change on elements you know will animate — not everything
- ✅ @media (prefers-reduced-motion) for accessibility
- ✅ Use DevTools Performance tab to find jank — look for long frames
- ✅ Transition: 300ms for hover, 500ms for page transitions
- ❌ Never animate width, height, padding, margin, top, left
- ❌ Never use will-change on every element — VRAM exhaustion
CSS animations connect to CSS Flexbox and Grid — layout understanding prevents animating properties that trigger it. External reference: web.dev CSS animations guide.
Recommended Reading
→ Designing Data-Intensive Applications — The essential book every senior developer needs.
→ The Pragmatic Programmer — Timeless engineering wisdom for writing better code.
Affiliate links. We earn a small commission at no extra cost to you.
Free Weekly Newsletter
🚀 Don’t Miss the Next Cheat Code
Join 1,000+ senior developers getting expert JS, Python, AWS and system design secrets weekly.
Related: CSS Pseudo-Classes and Pseudo-Elements: The Complete Styling Guide · Webpack and Vite: Modern JavaScript Bundling Explained for Application Developers · Python Dataclasses and Pydantic: Modern Data Validation for Production APIs
Related: Day 83: Multimodal Coding: Screenshots of Broken UIs · CSS Grid Advanced Patterns: Build Any Layout With Grid Template Areas · How to Build a Modern AWS Data Lake for Operational and Business Analytics with S3, Glue, Athena, Kinesis, and EventBridge
Watch: 100 Days of AI on YouTube
Short videos from CheatCoders, one AI engineering topic per day.
Most viewed
- Python Decorators Explained: From Simple Wrappers to Production Patterns
- Claude Code vs Cursor vs GitHub Copilot: Choose by Workflow, and Set Up One Repo That Works With All Three
- AI Agent Frameworks in 2025: LangGraph vs CrewAI vs AutoGen vs Raw API
- Python String Methods: Every str Method With Real Production Examples
- REST API Design Best Practices: The Patterns That Make APIs a Joy to Use
Newly added
- AWS Systems Manager Parameter Store: Hierarchical Runtime Config for Coding Agents
- Amazon Cognito Identity Pools: Federate Coding-Agent Tool Callers Without Long-Lived Keys
- Amazon Athena for Coding Agents: Query Tool Audit Logs and Eval Traces on S3 Without a Warehouse
- AWS Batch for Coding Agents: Parallel Overnight PR Reviews and Long-Running Jobs Without Lambda Timeouts
- Amazon Bedrock Prompt Management: Version and A/B Test Coding-Agent System Prompts
Deep-dive PDF
Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.
Already subscribed? or open the subscribe page.
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
