fixing-motion-performance
Fix animation performance issues.
how to use
Do not migrate animation libraries unless explicitly requested. Apply rules within the existing stack.
when to apply
Reference these guidelines when:
- adding or changing UI animations (CSS, WAAPI, Motion, rAF, GSAP)
- refactoring janky interactions or transitions
- implementing scroll-linked motion or reveal-on-scroll
- animating layout, filters, masks, gradients, or CSS variables
- reviewing components that use will-change, transforms, or measurement
rendering steps glossary
- composite: transform, opacity
- paint: color, borders, gradients, masks, images, filters
- layout: size, position, flow, grid, flex
rule categories by priority
| priority |
category |
impact |
| 1 |
never patterns |
critical |
| 2 |
choose the mechanism |
critical |
| 3 |
measurement |
high |
| 4 |
scroll |
high |
| 5 |
paint |
medium-high |
| 6 |
layers |
medium |
| 7 |
blur and filters |
medium |
| 8 |
view transitions |
low |
| 9 |
tool boundaries |
critical |
quick reference
1. never patterns (critical)
- do not interleave layout reads and writes in the same frame
- do not animate layout continuously on large or meaningful surfaces
- do not drive animation from scrollTop, scrollY, or scroll events
- no requestAnimationFrame loops without a stop condition
- do not mix multiple animation systems that each measure or mutate layout
2. choose the mechanism (critical)
- default to transform and opacity for motion
- use JS-driven animation only when interaction requires it
- paint or layout animation is acceptable only on small, isolated surfaces
- one-shot effects are acceptable more often than continuous motion
- prefer downgrading technique over removing motion entirely
3. measurement (high)
- measure once, then animate via transform or opacity
- batch all DOM reads before writes
- do not read layout repeatedly during an animation
- prefer FLIP-style transitions for layout-like effects
- prefer approaches that batch measurement and writes
4. scroll (high)
- prefer Scroll or View Timelines for scroll-linked motion when available
- use IntersectionObserver for visibility and pausing
- do not poll scroll position for animation
- pause or stop animations when off-screen
- scroll-linked motion must not trigger continuous layout or paint on large surfaces
5. paint (medium-high)
- paint-triggering animation is allowed only on small, isolated elements
- do not animate paint-heavy properties on large containers
- do not animate CSS variables for transform, opacity, or position
- do not animate inherited CSS variables
- scope animated CSS variables locally and avoid inheritance
6. layers (medium)
- compositor motion requires layer promotion, never assume it
- use will-change temporarily and surgically
- avoid many or large promoted layers
- validate layer behavior with tooling when performance matters
7. blur and filters (medium)
- keep blur animation small (<=8px)
- use blur only for short, one-time effects
- never animate blur continuously
- never animate blur on large surfaces
- prefer opacity and translate before blur
8. view transitions (low)
- use view transitions only for navigation-level changes
- avoid view transitions for interaction-heavy UI
- avoid view transitions when interruption or cancellation is required
- treat size changes as potentially layout-triggering
9. tool boundaries (critical)
- do not migrate or rewrite animation libraries unless explicitly requested
- apply these rules within the existing animation system
- never partially migrate APIs or mix styles within the same component
common fixes
/* layout thrashing: animate transform instead of width */
/* before */
.panel {
transition: width 0.3s;
}
/* after */
.panel {
transition: transform 0.3s;
}
/* scroll-linked: use scroll-timeline instead of JS */
/* before */
window.addEventListener('scroll', () => el.style.opacity = scrollY / 500)
/* after */ .reveal {
animation: fade-in linear;
animation-timeline: view();
}
// measurement: batch reads before writes (FLIP)
// before — layout thrash
el.style.left = el.getBoundingClientRect().left + 10 + "px";
// after — measure once, animate via transform
const first = el.getBoundingClientRect();
el.classList.add("moved");
const last = el.getBoundingClientRect();
el.style.transform = `translateX(${first.left - last.left}px)`;
requestAnimationFrame(() => {
el.style.transition = "transform 0.3s";
el.style.transform = "";
});
review guidance
- enforce critical rules first (never patterns, tool boundaries)
- choose the least expensive rendering work that matches the intent
- for any non-default choice, state the constraint that justifies it (surface size, duration, or interaction requirement)
- when reviewing, prefer actionable notes and concrete alternatives over theory
1---2name: fixing-motion-performance3description: Fix animation performance issues. Use when adding or changing UI animations, refactoring janky interactions, implementing scroll-linked motion, animating layout/filters/masks, or reviewing components that use will-change, transforms, or measurement.4---56# fixing-motion-performance78Fix animation performance issues.910## how to use1112- `/fixing-motion-performance`13 Apply these constraints to any UI animation work in this conversation.1415- `/fixing-motion-performance <file>`16 Review the file against all rules below and report:17 - violations (quote the exact line or snippet)18 - why it matters (one short sentence)19 - a concrete fix (code-level suggestion)2021Do not migrate animation libraries unless explicitly requested. Apply rules within the existing stack.2223## when to apply2425Reference these guidelines when:2627- adding or changing UI animations (CSS, WAAPI, Motion, rAF, GSAP)28- refactoring janky interactions or transitions29- implementing scroll-linked motion or reveal-on-scroll30- animating layout, filters, masks, gradients, or CSS variables31- reviewing components that use will-change, transforms, or measurement3233## rendering steps glossary3435- composite: transform, opacity36- paint: color, borders, gradients, masks, images, filters37- layout: size, position, flow, grid, flex3839## rule categories by priority4041| priority | category | impact |42| -------- | -------------------- | ----------- |43| 1 | never patterns | critical |44| 2 | choose the mechanism | critical |45| 3 | measurement | high |46| 4 | scroll | high |47| 5 | paint | medium-high |48| 6 | layers | medium |49| 7 | blur and filters | medium |50| 8 | view transitions | low |51| 9 | tool boundaries | critical |5253## quick reference5455### 1. never patterns (critical)5657- do not interleave layout reads and writes in the same frame58- do not animate layout continuously on large or meaningful surfaces59- do not drive animation from scrollTop, scrollY, or scroll events60- no requestAnimationFrame loops without a stop condition61- do not mix multiple animation systems that each measure or mutate layout6263### 2. choose the mechanism (critical)6465- default to transform and opacity for motion66- use JS-driven animation only when interaction requires it67- paint or layout animation is acceptable only on small, isolated surfaces68- one-shot effects are acceptable more often than continuous motion69- prefer downgrading technique over removing motion entirely7071### 3. measurement (high)7273- measure once, then animate via transform or opacity74- batch all DOM reads before writes75- do not read layout repeatedly during an animation76- prefer FLIP-style transitions for layout-like effects77- prefer approaches that batch measurement and writes7879### 4. scroll (high)8081- prefer Scroll or View Timelines for scroll-linked motion when available82- use IntersectionObserver for visibility and pausing83- do not poll scroll position for animation84- pause or stop animations when off-screen85- scroll-linked motion must not trigger continuous layout or paint on large surfaces8687### 5. paint (medium-high)8889- paint-triggering animation is allowed only on small, isolated elements90- do not animate paint-heavy properties on large containers91- do not animate CSS variables for transform, opacity, or position92- do not animate inherited CSS variables93- scope animated CSS variables locally and avoid inheritance9495### 6. layers (medium)9697- compositor motion requires layer promotion, never assume it98- use will-change temporarily and surgically99- avoid many or large promoted layers100- validate layer behavior with tooling when performance matters101102### 7. blur and filters (medium)103104- keep blur animation small (<=8px)105- use blur only for short, one-time effects106- never animate blur continuously107- never animate blur on large surfaces108- prefer opacity and translate before blur109110### 8. view transitions (low)111112- use view transitions only for navigation-level changes113- avoid view transitions for interaction-heavy UI114- avoid view transitions when interruption or cancellation is required115- treat size changes as potentially layout-triggering116117### 9. tool boundaries (critical)118119- do not migrate or rewrite animation libraries unless explicitly requested120- apply these rules within the existing animation system121- never partially migrate APIs or mix styles within the same component122123## common fixes124125```css126/* layout thrashing: animate transform instead of width */127/* before */128.panel {129 transition: width 0.3s;130}131/* after */132.panel {133 transition: transform 0.3s;134}135136/* scroll-linked: use scroll-timeline instead of JS */137/* before */138window.addEventListener('scroll', () => el.style.opacity = scrollY / 500)139/* after */ .reveal {140 animation: fade-in linear;141 animation-timeline: view();142}143```144145```js146// measurement: batch reads before writes (FLIP)147// before — layout thrash148el.style.left = el.getBoundingClientRect().left + 10 + "px";149// after — measure once, animate via transform150const first = el.getBoundingClientRect();151el.classList.add("moved");152const last = el.getBoundingClientRect();153el.style.transform = `translateX(${first.left - last.left}px)`;154requestAnimationFrame(() => {155 el.style.transition = "transform 0.3s";156 el.style.transform = "";157});158```159160## review guidance161162- enforce critical rules first (never patterns, tool boundaries)163- choose the least expensive rendering work that matches the intent164- for any non-default choice, state the constraint that justifies it (surface size, duration, or interaction requirement)165- when reviewing, prefer actionable notes and concrete alternatives over theory