Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.
CSSHigh

Use transform and opacity for animations

Animate with CSS transform and opacity properties to keep animations running on the GPU compositor thread at 60fps, avoiding layout-triggering properties like top, left, width, and height.

Utilities
Quick take
Typical fix time 20 min
  • Only transform and opacity can be composited on the GPU without triggering layout
  • Use will-change: transform sparingly to promote an element to its own compositor layer
  • Prefer CSS transitions and animations over JavaScript animation libraries for simple effects
  • Respect prefers-reduced-motion to disable animations for users who need it
Why it matters: Animating layout properties (width, height, top, margin) triggers the full browser rendering pipeline on every frame — style recalculation, layout, paint, and composite. This runs on the main thread and competes with JavaScript. Animating transform and opacity skips layout and paint entirely and runs on a dedicated GPU thread, achieving smooth 60fps even when the main thread is busy.

Rule Details

The browser renders pages through a pipeline: Style → Layout → Paint → Composite. Animating the wrong properties restarts this pipeline every frame. Animating transform and opacity only triggers the composite step.

Code Example

Layout-triggering properties (avoid animating):
  width, height, margin, padding, top, left, right, bottom,
  border-width, font-size, display, position
 
Paint-triggering properties (acceptable, but not ideal):
  color, background-color, box-shadow, border-color,
  outline, text-shadow
 
Compositor-only properties (optimal for animation):
  transform (translate, scale, rotate, skew)
  opacity
  filter (on composited layers)

Why It Matters

Animating layout properties (width, height, top, margin) triggers the full browser rendering pipeline on every frame — style recalculation, layout, paint, and composite. This runs on the main thread and competes with JavaScript. Animating transform and opacity skips layout and paint entirely and runs on a dedicated GPU thread, achieving smooth 60fps even when the main thread is busy.

Converting Position Animations

/* ❌ Bad: animates layout properties — triggers full reflow each frame */
.slide-in {
  animation: slideIn 300ms ease-out;
}
 
@keyframes slideIn {
  from { left: -100%; }
  to { left: 0; }
}
 
/* ✅ Good: translate() is compositor-only */
.slide-in {
  animation: slideIn 300ms ease-out;
}
 
@keyframes slideIn {
  from { transform: translateX(-100%); }
  to { transform: translateX(0); }
}

Converting Size Animations

/* ❌ Animates width/height — expensive */
.expand {
  animation: expand 300ms ease;
}
@keyframes expand {
  from { width: 0; height: 0; }
  to { width: 200px; height: 200px; }
}
 
/* ✅ Use scale() */
.expand {
  width: 200px;
  height: 200px;
  animation: expand 300ms ease;
}
@keyframes expand {
  from { transform: scale(0); }
  to { transform: scale(1); }
}

Common Performant Patterns

/* Fade in */
.fade-in {
  animation: fadeIn 300ms ease;
}
@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}
 
/* Slide up and fade in */
.slide-up {
  animation: slideUp 400ms cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes slideUp {
  from {
    opacity: 0;
    transform: translateY(16px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
 
/* Button press */
.button:active {
  transform: scale(0.97);
}

will-change (Use Sparingly)

/* Hint to browser to promote this element to its own layer before animation starts */
.animated-card {
  will-change: transform;
}
 
/* ⚠️ Don't apply to too many elements — each layer uses GPU memory */
/* Apply just before animation, remove after */

Respecting Reduced Motion

/* ✅ Always honor the user's motion preferences */
.slide-in {
  animation: slideIn 400ms ease;
}
 
@media (prefers-reduced-motion: reduce) {
  .slide-in {
    animation: none;
    /* Provide a non-animated alternative if needed */
  }
}
 
/* Or use the safe pattern */
@media (prefers-reduced-motion: no-preference) {
  .slide-in {
    animation: slideIn 400ms ease;
  }
}

Verification

  1. Inspect the rendered UI at the breakpoints and interaction states affected by the rule.
  2. Confirm the computed styles match the intended fix in DevTools.
  3. Test at least one mobile and one desktop viewport before shipping.
  4. If the rule affects motion, contrast, or layout stability, verify those user-facing outcomes directly.

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Find CSS animations or transitions in this file that animate layout-triggering properties like top, left, width, height, or margin. Flag them for optimization.

Fix

Auto-fix issues

Convert layout-triggering animations to use transform equivalents: position changes to translate(), size changes to scale().

Explain

Learn more

Explain the browser rendering pipeline, why transform and opacity are performant for animation, and how the GPU compositor works.

Review

Code review

Review stylesheets, component styles, and responsive states related to Use transform and opacity for animations. Flag exact selectors, declarations, or breakpoints that violate the rule in the rendered UI.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

Chrome DevTools Rendering tabdeveloper.chrome.comTool
MDN prefers-reduced-motiondeveloper.mozilla.orgTool

Rules that often go hand-in-hand with this one.

Use CSS containment to limit repaint scope

Apply the contain property to components to tell the browser they are independent from the rest of the page, enabling rendering optimizations that reduce repaint and reflow scope.

CSS
Do not disable pinch zoom

The viewport meta tag must not set user-scalable=no or maximum-scale=1 as these prevent users from zooming in to read content, violating WCAG 2.1 SC 1.4.4 (Resize Text).

CSS
Set explicit width and height on images

All <img> elements have explicit width and height attributes so browsers can reserve space before the image loads, preventing layout shift.

Images
Use the View Transitions API for smooth page and component transitions

The View Transitions API is used to animate between page states or navigations with cross-fade or custom animations, providing a native-app quality transition without JavaScript animation libraries.

CSS

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385