will-change promotes an element to its own GPU compositing layer (ISO-25010 resource-utilisation). Used correctly on a single animating modal, that is a genuine win. Applied globally in CSS to 20+ static elements — headers, footers, card wrappers that never move — it creates 20+ permanent GPU layers, consuming VRAM on every device. On mobile with limited VRAM, this causes compositor memory pressure, degrades scroll performance, and can make the browser drop frames on animations it would otherwise handle smoothly. Misuse is the opposite of its intended effect.
Low because overuse of `will-change` degrades GPU memory and scroll performance rather than breaking functionality, but the degradation is measurable and worsens on low-end devices.
Audit every will-change declaration and remove any that target non-animating elements:
grep -r "will-change" src/
For elements that animate occasionally (modals, tooltips, drawers), apply and remove will-change in JavaScript around the animation rather than in permanent CSS:
// src/components/modal.tsx
function animateOpen(el: HTMLElement) {
el.style.willChange = 'opacity, transform' // hint before animation
el.animate(
[{ opacity: 0, transform: 'scale(0.95)' }, { opacity: 1, transform: 'scale(1)' }],
{ duration: 200 }
).finished.then(() => {
el.style.willChange = 'auto' // release the layer after animation
})
}
Keep active will-change declarations to 3–5 elements simultaneously. Static containers, page wrappers, and <section> elements must not have will-change set.
ID: performance-core.rendering-paint.will-change
Severity: low
What to look for: Count all relevant instances and enumerate each. Search CSS files, styled-components, and inline styles for will-change declarations. For each occurrence, verify that the element actually animates or transforms during normal use. Check whether will-change is applied to many elements at once (more than 5-10), to non-animating elements, or set permanently in CSS rather than added/removed in JavaScript before and after animations. Look for will-change: transform or will-change: opacity on static containers or large layout elements that do not move.
Pass criteria: will-change is applied only to elements that genuinely undergo GPU-compositable transforms or opacity changes (e.g., animated modals, tooltips, floating buttons). It is added in JavaScript immediately before an animation starts and removed immediately after, rather than declared permanently in CSS. No more than 3-5 elements have will-change active simultaneously.
Fail criteria: will-change is declared in CSS on many elements (10+) unconditionally, including elements that never animate. Static layout containers, wrapper divs, or entire sections use will-change: transform. The property is treated as a blanket performance improvement applied everywhere rather than a targeted hint.
Skip (N/A) when: The project has no animations, transitions, or GPU-composited effects — making will-change irrelevant.
Detail on fail: Specify the misuse. Example: "will-change: transform applied to 23 elements in globals.css including static headers, footers, and card wrappers that never animate. Creates 23 separate GPU layers, increasing VRAM usage and harming scroll performance" or "will-change set permanently on all .btn elements; buttons are clicked but not continuously animated — property should be applied on hover/focus and removed after interaction".
Remediation: will-change hints to the browser that an element is about to be transformed, allowing it to create a GPU compositing layer in advance. Overuse creates too many layers and harms performance. Apply it surgically:
CSS (permanent, acceptable for elements that always animate):
/* Good: single animating element promoted to GPU layer */
.modal-overlay {
will-change: opacity; /* fades in/out on open/close */
}
/* Bad: static element with will-change */
.page-header {
will-change: transform; /* never moves — remove this */
}
JavaScript (preferred for elements that animate occasionally):
function animateElement(el: HTMLElement) {
el.style.willChange = 'transform' // hint before animation
el.animate([
{ transform: 'translateY(0)' },
{ transform: 'translateY(-20px)' }
], { duration: 300 }).finished.then(() => {
el.style.willChange = 'auto' // remove hint after animation
})
}
Audit existing usage: grep -r "will-change" src/ and review every result to confirm the element actually animates.
Cross-reference: For related patterns and deeper analysis, see the corresponding checks in other AuditBuffet audits covering this domain.