Box Shadow Performance: Tips for Smooth Animations
Box shadows are generally performant, but they can cause issues when overused or animated incorrectly. Here's how to use shadows efficiently.
How Browsers Render Shadows
Box shadows are painted during the browser's paint phase. This means:
Common Performance Issues
Animating Box Shadow Directly
This is expensive and can cause jank:
.card:hover {
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
transition: box-shadow 0.3s ease;
}
Each frame requires a full repaint of the shadow.
Too Many Shadowed Elements
Having hundreds of elements with complex shadows can slow down scrolling.
Large Blur Radius
Blur is computationally expensive. A 100px blur is much more costly than a 10px blur.
Better Animation Techniques
Use Opacity Instead
Create both shadow states and animate opacity:
.card {
position: relative;
}
.card::after {
content: '';
position: absolute;
inset: 0;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
border-radius: inherit;
}
.card:hover::after {
opacity: 1;
}
Opacity changes are GPU-accelerated and much smoother.
Use Transform for Lift Effects
Instead of changing shadow size, move the element:
.card {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
}
.card:hover {
transform: translateY(-4px);
}
The shadow stays the same, but the lift creates the illusion of more depth.
Combine Both Techniques
.card {
position: relative;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
}
.card::after {
content: '';
position: absolute;
inset: 0;
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
border-radius: inherit;
z-index: -1;
}
.card:hover {
transform: translateY(-2px);
}
.card:hover::after {
opacity: 1;
}
Optimization Tips
1. Limit Shadow Complexity
Use 1-3 shadow layers instead of 5+. The visual difference is often minimal.
2. Reduce Blur on Mobile
Mobile GPUs are less powerful. Consider simpler shadows for mobile:
@media (max-width: 768px) {
.card {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
}
3. Use will-change Sparingly
will-change: box-shadow can help, but overuse causes memory issues.
4. Consider CSS Containment
For complex UIs, contain: paint can limit repaint areas:
.card {
contain: paint;
}
5. Avoid Shadows on Scrolling Elements
Shadows on elements that scroll with the page are fine. Shadows on fixed/sticky elements that stay visible while content scrolls can cause issues.
Testing Performance
Use Chrome DevTools to identify shadow performance issues:
1. Open DevTools > Performance tab
2. Record while interacting with shadowed elements
3. Look for long paint times in the flame chart
4. Enable "Paint flashing" in Rendering tab to visualize repaints
Conclusion
Shadows are powerful but require consideration for performance. Use the pseudo-element opacity technique for animations, keep blur values reasonable, and test on lower-powered devices. ShadowSpark helps you design shadows visually so you can focus on getting the effect right.