# Why Only Animate Transform and Opacity for Performance: The Complete Guide

> Unlock smooth 60fps animations by only animating transform and opacity. Learn how these properties leverage the GPU to bypass layout and paint, boosting web performance.

- Repository: [Emil Kowalski/skills](https://github.com/emilkowalski/skills)
- Tags: performance
- Published: 2026-07-29

---

**You should only animate `transform` and `opacity` because these are the only CSS properties that run entirely on the GPU, bypassing the browser's expensive layout and paint phases to maintain smooth 60fps motion.**

Animating the wrong CSS properties can cripple your application's frame rate and introduce visible jank. According to the `emilkowalski/skills` repository architecture guidelines, restricting animations to `transform` and `opacity` is the only reliable path to achieving hardware-accelerated motion without blocking the main thread or forcing costly style recalculations.

## Understanding the Browser Rendering Pipeline

Modern browsers render web pages through a multi-step pipeline: **layout** (calculating geometry), **paint** (filling pixels), and **composite** (drawing layers to the screen). Most CSS properties force the browser to execute all three steps on every animation frame. However, `transform` and `opacity` are unique compositor-only properties that skip directly to the final composite step on the GPU.

When you animate layout-affecting properties like `width`, `height`, `margin`, or `position` coordinates, the browser must recalculate the entire page geometry, repaint the affected regions, and then composite. This triple-phase execution saturates the main thread and drops frames under heavy load.

## Why Transform and Opacity Are GPU-Only

The `emilkowalski/skills` repository explicitly documents this performance boundary in [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md):

> "Only animate `transform` and `opacity` — they skip layout/paint and run on the GPU. `padding`/`margin`/`height`/`width`/`top`/`left` trigger all three rendering steps."

### Transform Properties

The **`transform`** property manipulates an element's position, scale, rotation, or 3D orientation entirely within the compositor. Because these geometric changes don't affect the document flow or neighboring elements, the browser can batch these updates on the GPU without consulting the main thread's layout engine.

### Opacity Changes

Like `transform`, **`opacity`** adjustments occur during the composite phase. The browser simply adjusts the alpha channel of an existing layer rather than recalculating pixel values across the render tree. This makes opacity transitions extremely cheap even on complex DOM structures.

## The Cost of Layout-Triggering Properties

Animating properties that affect layout forces the browser into a performance bottleneck known as **layout thrashing**. The following table illustrates the rendering cost difference:

| Property | Rendering Phase | Performance Impact |
|----------|----------------|------------------|
| **`transform`** | Composite only | **Fast** – GPU-only, minimal main-thread work |
| **`opacity`** | Composite only | **Fast** – GPU-only, no re-layout |
| `width`, `height`, `top`, `left` | Layout + Paint + Composite | **Slow** – forces complete render tree recalculation |

When animating `height` or `width`, the browser must answer geometry questions like "What size is this element now?" and "Where do surrounding elements move?" on every frame. This synchronous layout calculation blocks JavaScript execution and destroys animation smoothness.

## Repository Standards and Guidelines

The [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) file codifies this rule at lines 112-113, establishing it as a hard constraint for all animations in the codebase. This standard ensures that animations remain interruptible and resilient to page load conditions.

Additional constraints appear throughout the repository:

- **[`skills/review-animations/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/SKILL.md)** summarizes the rule in checklist format for quick reference during development.
- **[`skills/improve-animations/AUDIT.md`](https://github.com/emilkowalski/skills/blob/main/skills/improve-animations/AUDIT.md)** demonstrates how to audit existing codebases for violations of the transform/opacity rule.
- **[`skills/emil-design-eng/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/emil-design-eng/SKILL.md)** provides concrete before-and-after examples of property migrations.

## Practical Code Examples

The repository provides specific implementation patterns for both vanilla CSS and React animation libraries.

### Correct: GPU-Only CSS Animation

Animate `transform` and `opacity` to fade and slide elements without layout impact:

```css
.item {
  opacity: 0;
  transform: translateY(8px);
  animation: fadeIn 300ms ease-out forwards;
}

@keyframes fadeIn {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

```

### Incorrect: Layout-Triggering Animation

Never animate dimensional properties that force reflow:

```css
/* Bad: animating height forces layout + paint on every frame */
.box {
  height: 0;
  animation: grow 400ms ease-out forwards;
}

@keyframes grow {
  to {
    height: 200px;  /* Triggers layout recalculation */
  }
}

```

### Framer Motion Implementation

When using Framer Motion, always use the full **`transform`** string rather than shorthand properties:

```jsx
import { motion } from "framer-motion";

// ✅ Correct: Uses compositor thread
<motion.div animate={{ transform: "translateX(100px)" }} />

```

```jsx
// ❌ Incorrect: Runs on main thread via requestAnimationFrame
<motion.div animate={{ x: 100 }} />

```

The shorthands `x`, `y`, and `scale` execute via JavaScript on the main thread, while the complete `transform` string leverages the browser's native compositor as documented in [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) (lines 118-122).

## Advanced Performance Considerations

Beyond property selection, the repository identifies two additional anti-patterns that negate GPU acceleration.

### Avoid CSS Variable Animation on Parents

Updating CSS custom properties on parent elements forces style recalculation for all descendants, effectively bypassing the GPU optimization. The standards file warns against this pattern at lines 113-117, noting that animating a variable like `--x` via `@property` still triggers layout if it affects geometry.

### Framer Motion Shorthand Limitations

As noted in the source analysis, Framer Motion's shorthand properties (`x`, `y`, `scale`) bypass the compositor and run through `requestAnimationFrame`. For high-performance scenarios, always expand these to their `transform` string equivalents to ensure the animation stays on the GPU.

## Summary

- **Only animate `transform` and `opacity`** to ensure GPU-only compositing and avoid layout/paint costs.
- **Layout properties** like `width`, `height`, `margin`, and `position` coordinates force expensive recalculation of page geometry on every frame.
- **Use full transform strings** in animation libraries like Framer Motion rather than shorthand properties to maintain hardware acceleration.
- **Never animate CSS variables** on parent elements, as this forces style recalculation across descendant trees and negates GPU benefits.

## Frequently Asked Questions

### What happens if I animate width or height instead of transform?

Animating `width` or `height` forces the browser to execute the complete rendering pipeline—layout, paint, and composite—on every animation frame. This recalculates the position and size of the animated element and potentially its siblings, blocking the main thread and causing visible stutter or "jank" during the animation.

### Why are Framer Motion shorthands like `x` and `y` slower than transform strings?

Framer Motion shorthands (`x`, `y`, `scale`) are implemented via JavaScript's `requestAnimationFrame` running on the main thread, while the full `transform` CSS string is handled natively by the browser's compositor on the GPU. Under heavy load or concurrent animations, the main-thread approach drops frames, whereas the compositor maintains smooth motion.

### Can I use `will-change` to accelerate other properties?

While `will-change` can hint to the browser to prepare layers for animation, it does not eliminate the layout and paint costs for properties like `width` or `top`. The `emilkowalski/skills` repository standards explicitly reject this optimization for layout properties, insisting that only `transform` and `opacity` provide guaranteed GPU-only rendering paths.

### How do CSS variables affect animation performance?

Animating CSS custom properties (variables) on parent elements forces the browser to recalculate styles for all descendants, effectively negating the GPU acceleration benefits. Even if the variable controls a transform value, updating the variable itself triggers style invalidation across the subtree, moving work back to the main thread.