
Laggy animations are almost always caused by properties that trigger re-layout — like width, height, margin, or top. Every frame, the browser must recalculate every element position. Expensive.
The golden rule: animate only transform and opacity. Both run on the GPU in the compositor layer, without touching layout at all. translateY(-4px) is always cheaper than margin-top: -4px.
/* slow: triggers layout */
.card:hover { margin-top: -4px; }
/* fast: compositor only */
.card:hover { transform: translateY(-4px); }For scroll animations, do not use raw scroll events — use IntersectionObserver. The browser decides when to fire the callback, far more efficient than computing positions every frame.
And do not forget accessibility: wrap all non-essential animations in @media (prefers-reduced-motion: reduce) and disable them. Some users genuinely need a still website.
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}This very website uses all these techniques: scroll reveal, hover lift, and everything fully disabled for reduced-motion users. Speed is a feature.

