On this page
Animation jank is a symptom, not a diagnosis. A stuttering card might be repainting a large shadow, competing with JavaScript, or uploading oversized imagery. Replacing every property with a transform can help some effects while leaving the actual bottleneck untouched.
Create a repeatable motion benchmark
Choose one slow interaction and record the exact steps needed to reproduce it. Keep viewport dimensions, content, and device conditions consistent. A trace captured while opening a small empty menu is not comparable to one captured while a populated drawer loads images.
Prepare a control version with motion disabled and a second version with the effect enabled. If both are slow, investigate rendering or application work before blaming interpolation. Use a realistic elevation sample rather than a huge artificial blur that makes the test dramatic but unrepresentative.
Use the box shadow generator to prepare a static visual reference before tuning movement. Export the relevant values into your prototype and keep them fixed during the first timing comparison. This separates appearance decisions from playback behavior and makes changes easier to judge. The tool prepares the artwork or layout; the animation code still owns the timeline.
Define a baseline state that works without animation. Check that its content, controls, and status remain understandable when no intermediate frames are shown. A successful effect connects valid states; it does not hide essential information inside a brief moment that users must catch.
Map properties to rendering work
Changing width or top can trigger layout; changing shadows or backgrounds often triggers paint. Transform and opacity can often be handled by compositing, but the browser still has to create and manage the relevant surfaces. Compositor-friendly does not mean resource-free.
For elevation, compare interpolating a large shadow with crossfading a fixed shadow on a pseudo-element. The latter can avoid repainting the shadow each frame once the surface is available, although layer size and browser decisions still matter. Keep the pseudo-element behind content and away from hit testing.
.card { position: relative; isolation: isolate; }
.card::before {
content: "";
position: absolute;
inset: 0;
z-index: -1;
border-radius: inherit;
box-shadow: 0 12px 30px rgb(0 0 0 / .2);
opacity: 0;
pointer-events: none;
transition: opacity 180ms ease-out;
}
.card[data-raised="true"]::before { opacity: 1; }Treat this example as a focused implementation pattern, not a complete component. Connect its selectors and lifecycle to your actual application state, and preserve the same outcome when playback is skipped. For terminology and API details, consult MDN’s rendering performance overview.
Read the trace before changing code
Record the interaction in the performance panel and inspect long frames. Look for scripting, style recalculation, layout, paint, and compositing activity. A long JavaScript task that overlaps the animation may be the real cause even if the visible object is pure CSS.
Enable paint flashing to see which regions repaint. If moving a small badge repaints a large ancestor, inspect effects and containment boundaries. If the animation looks smooth but input is delayed, evaluate main-thread responsiveness separately. A frame-rate counter cannot explain whether the next click was handled promptly.
Use one-variable comparisons during review. Hold the distance constant while changing duration, then hold duration constant while changing the curve. Otherwise a reviewer may prefer the smaller movement rather than the timing change you intended to evaluate. Capture the chosen values beside the prototype so the production version does not rely on visual guesswork.
Related motion and animation guides can help place this effect in a wider motion vocabulary. Consistency should mean similar behavior for similar tasks, not forcing every component to travel the same distance or share one universal duration.
Use layers and will-change selectively
The will-change property is a hint, not a general acceleration switch. Applying it permanently to hundreds of cards can increase memory use and create too many layers. Introduce it only when a measured effect benefits, ideally shortly before movement, and remove it when no longer useful.
Check surface area as well as layer count. A full-screen translucent panel can be expensive despite being a single layer. Oversized offscreen decorations, filters, and high-resolution images may still require substantial work. Reduce geometry or effect complexity before assuming another hint will solve the problem.
Review the relevant platform behavior in MDN’s will-change reference. Where support is incomplete, prefer an immediately usable final state over a complicated fallback that introduces its own timing bugs. Feature detection should gate the enhancement, not access to the underlying task.
Verify improvements on a weaker device
Repeat the same trace after one targeted change. Record what improved: fewer paint events, less layout time, or reduced scripting. If the trace is unchanged, reconsider the hypothesis instead of keeping complexity that merely looks performance-conscious.
Test under modest CPU conditions and on an actual lower-powered device when available. Watch sustained loops long enough to reveal thermal or battery concerns. Also test disabled motion: removing an animation should not reveal content that was accidentally dependent on animation fill mode or leave an overlay intercepting input.
Run a production-content pass after the isolated demo succeeds. Use long labels, realistic images, narrow containers, and enough neighboring elements to expose overlap. Repeat with keyboard navigation and at least one touch device. A demonstration that works only when the pointer stays still is not a reliable interaction.
Record a short before-and-after trace using the guidance in web.dev’s animation guide. Compare the same interaction with motion enabled and disabled. Look for repeated layout, large paint regions, and delayed input rather than judging performance solely from a smooth recording on a powerful development machine.
Keep a concise acceptance note with the trigger, final state, cancellation policy, and reduced-motion behavior. The broader development collection is useful for implementation context, but the acceptance note should be specific enough that another developer can reproduce this effect without watching a video.
Key takeaways
- Diagnose rendering work instead of guessing from appearance.
- Compare identical interactions before and after each change.
- Treat compositing and layer promotion as limited resources.
- Verify both frame delivery and input responsiveness.
Reach designers & developers at work
Put your product next to the tools people use every day. One sponsor per placement, no tracking pixels.
Become a sponsor