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:


  • Changes to shadows trigger repaints
  • Large blur values require more processing
  • Multiple shadows multiply the rendering cost

  • 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.

    Ready to create your own shadows?

    Try ShadowSpark and design beautiful CSS box shadows instantly.

    Try ShadowSpark Free