In this article, learn how to animate CSS box shadows without slowing browser performance.
In CSS, box-shadow
The property is used to add shadows to web elements and these shadows can be animated. However, shadow animations can impact browser performance and cause delays when rendering pages.
This guide is intended for front-end developers with a working knowledge of HTML and CSS animations.
Why this matters
Web page load times should be very short, ideally less than 5 seconds. Studies show that faster page loads significantly improve conversion rates. According to further research, he 70% of users said that website speed affects their willingness to buy from online stores. Basically, a fast site means happy users.
Before we go any further, here’s a demo of how box-shadow
Animation works on web pages. You can scroll and interact with web elements.
look at the pen
Web elements with shadow animations by SitePoint (@SitePoint)
with a code pen.
3 main CSS box shadow animation events
Due to what is going on behind the scenes, CSS box shadow animations can be resource intensive. There are three main processes or events that are triggered during a box shadow animation (or any form of animation). These events are paint, layout and compositing.
-
paintingWith paint, the browser fills the pixels with color.
box-shadow
One of the CSS properties that trigger this event. Basically it creates a new shadow on every frame of the animation. According to Mozilla, ideal CSS animations should run at 60fps. -
layoutSome animations change the structure of the page, so many styles may need to be recalculated. A good example is a sidebar that pushes other elements away when expanded. CSS properties that cause this include
padding
,margin
,border
.Simply put, when animated properties affect other elements, they change the layout of the page, cause recalculations, and use a lot of system resources.
-
synthesisIn compositing only part of the page is changed.CSS properties like
opacity
andtransform
Affects only the element to which they are applied. This results in less style recalculation and smoother animation. Synthesis is the process with the fewest tasks of all three processes.
You can observe this process in real time using your browser’s inspector tool. First, open the inspector tool (Chrome is pictured below) and click the three dots in the top right corner of the tab.check Other tools and choose rendering.
In this example, flashing paint is selected. The screen flashes green whenever a paint event occurs.
- Navigation bar:
- Text card:
- Navigation link:
Hover over any element with a shadow or refresh the page and it will flash green. You can also do the same experiment with layouts.just uncheck flashing paint and choose layout shift area.
Note that Flash Paint may not work in CodePen demos. So try this out with a live preview from your text editor. The video below shows what you should watch.
The goal is to minimize painting and layout changes as it uses more system resources.
Check performance
Developers have fast computers and may have no problem running shadow animations. However, we have to consider the user with his slow PC and unreliable internet connection. Just because it looks good on your computer doesn’t mean it will look the same elsewhere.
a box-shadow
has four values and a color. These four values are the shadow’s horizontal position (x offset), vertical position (y offset), spread, and blur radius respectively. A typical shadow animation changes one or more of the following values:
box-shadow: <x-offset> <y-offset> <spread> <blur> <color>;
Let’s make it easily box-shadow
Animation starting with some HTML:
<body>
<div class="box"></div>
</body>
Here’s the CSS for the first and last shadow:
.box {
box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.5);
transition: transform ease 0.5s, box-shadow ease 0.5s;
}
.box:hover {
transform: translateY(-5px);
box-shadow: 0px 10px 20px 2px rgba(0, 0, 0, 0.25);
}
The results are as follows.
look at the pen
Animated box shadow with SitePoint (@SitePoint)
with a code pen.
The animation changes the y-offset, blur, and spread values. Also use a more transparent final shadow.
Let’s take a look at what’s going on behind the scenes in doing this. 0.5s
animation.In your browser, right-click and select Open developer tools inspect. When the tool opens, performance tab. You can record shadow animations. Just a few seconds is enough to see what’s going on.
The screenshot below shows what you get from Chrome’s dev tools.
The animation duration of the shadow hovering up and down is highlighted at the top and a breakdown of the process occurring is shown at the bottom. The breakdown shows that it takes 7 ms to script, 55 ms to render, and 30 ms to paint.
These numbers look fine, but what if the CPU is four times slower?[パフォーマンス]You can adjust the CPU speed from the tab.
The following illustration shows what happens when the same animation runs on a slow CPU.
This new process takes 6 ms to load. Scripting is now up to 52ms, rendering more than doubled to 117ms, and painting to 72ms.
You can also adjust the network speed to make the CPU even slower. Shadow animations use a lot of system resources, so consider removing some of the cost.
it is important to note transform
properties affect CPU performance. More on this later.
How to maintain optimal performance
If your web page needs to animate shadows, the performance gain is worth it. In this section, you will learn various ways to tweak shadow animations to reduce the performance hit.
It describes:
- animation of opacity
- plural
box-shadow
layer - Animation of pseudo-elements
- using
transform
property
animation of opacity
while using it rgba
Color, alpha channel controls opacity. Changing only the alpha channel when animating a shadow is less CPU intensive than changing the shadow’s offset and spread values.
.box {
box-shadow: 0px 10px 20px 0px rgba(0, 0, 0, 0.5);
transition: box-shadow ease 0.5s;
}
.box:hover {
box-shadow: 0px 10px 20px 0px rgba(0, 0, 0, 0.25);
}
.box-2 {
box-shadow: 0px 10px 20px 0px rgba(0, 0, 0, 0.5);
transition: box-shadow ease 0.5s;
}
.box-2:hover {
box-shadow: 0px 20px 40px 0px rgba(0, 0, 0, 0.25);
}
In the first animation only the opacity of the shadow is changing, but in the second animation the y offset is 10px
To 20px
the spread is varying from 20px
To 40px
.
And here’s how it performs (so you can clearly see the performance graph) with a 6x slowdown, starting with an animation where only the opacity changes.
It takes about 2 seconds to hover over the box. Compare this to the second shadow animation.
Again, cycling on and off for 2 seconds shows a noticeable increase in the time of all events. Paint used to be 96ms, now he doubles to 187ms. Rendering, which is part of compositing, has also been reduced from 97ms to 178ms.
So changing only shadow opacity will improve animation performance.
Here’s a live demo of these two animations.
look at the pen
Animated Opacity and Animated Offset by SitePoint (@SitePoint)
with a code pen.
layered shadow
If you observe the shadows around the table or lift an object above it, you’ll see that the darkest shadowed areas are closest to the object and get brighter and lighter as they spread out.
This effect is not easy to reproduce box-shadow
Layered shadows look much better. It also improves animation performance, even when shadow he layers are added.
Let’s compare the performance of the unit box-shadow
and multilayer shadows:
.box {
box-shadow: 0px 10px 20px 0px rgba(0, 0, 0, 0.5);
transition: box-shadow ease 0.5s;
}
.box:hover {
box-shadow: 0px 20px 40px 0px rgba(0, 0, 0, 0.25);
}
This animation takes 148ms to render and 133ms to paint.
Now let’s create two shadow animations. box-shadow
layer:
.box-2 {
box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.25),
0px 10px 20px 0px rgba(0, 0, 0, 0.5);
transition: box-shadow ease 0.5s;
}
.box-2:hover {
box-shadow: 0px 10px 20px 0px rgba(0, 0, 0, 0.25),
0px 20px 40px 0px rgba(0,0,0,0.15);
}
The difference is clear. Layered shadows not only produce nice-looking shadow effects, but they perform surprisingly well when animated. Rendering was reduced from 148ms to 74ms and painting he was reduced from 133ms to 74ms.
Here are two live demos compared.
look at the pen
Animating single and layered shadows with SitePoint (@SitePoint)
with a code pen.
Now let’s try a different method by adding a second shadow during the animation.
.box-2 {
box-shadow: 0px 10px 20px 0px rgba(0, 0, 0, 0.5);
transition: box-shadow ease 0.5s;
}
.box-2:hover {
box-shadow: 0px 10px 20px 0px rgba(0, 0, 0, 0.25),
0px 20px 40px 0px rgba(0,0,0,0.15);
}
Adding a second shadow layer during animation is not as performant as using two layers to begin with, but it still requires 100ms of painting compared to 133ms for a single one. is. box-shadow
Improved animation.
Ultimately, it’s up to you to decide what your shadows look like and what methods you use to create them.
Animating pseudo-elements
This time, we duplicate the shadow animation without changing it. box-shadow
property. From the previous demo, you can see that there is still a lot of redrawing going on during the shadow animation.When to change box-shadow
This process cannot be avoided.
At the end of this section you will see the almost complete elimination of paint. It requires more lines of code, but allows for more performant shadow animation.
So after basic styling of the box, :after
Give it a pseudo-element box-shadow
to be the final state of the shadow after animation.
.pseudo {
position: relative;
transition: box-shadow ease 0.5s, transform ease 0.5s;
box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.5)
}
.pseudo::after{
content: "";
position: absolute;
border-radius: 20px;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
box-shadow: 0px 10px 20px 2px rgba(0, 0, 0, 0.25);
transition: opacity ease 0.5s;
}
Now all you have to do is opacity
of the above pseudo-element :hover
:
.pseudo:hover{
transform: translateY(-10px);
}
.pseudo:hover::after{
opacity: 1;
}
Let’s see it side by side with the normal shadow animation.
look at the pen
SitePoint Pseudo Shadow (@SitePoint)
with a code pen.
There isn’t much to go on visually here. The real difference lies in their performance.Regular result box-shadow
Animation is shown below.
Render time is 230 ms and paint time is 211 ms. Pseudo shadow animation this time.
This time it takes only 148ms to render and 51ms to paint. It’s more code, but the results are worth the effort.
using transform
property
This mainly applies to boxes, which are the main elements with shadows.using transform
Instead of layout changing properties like property margin
to reduce the amount of style recalculation.
This property is translate
again scale
Use properties to simulate lifting an element off the page, creating the illusion of depth.
helpful tips
An animation containing is already established. box-shadow
Properties affect performance. So, if you want CSS box-shadow animations, here are some useful tips to keep in mind.
First, minimize them. Don’t cast shadows on all elements because of it. Then animate only the interactive elements. No need to animate non-functional things. This reduces CPU load and significantly improves performance.
Conclusion
Shadows visually enhance your site, but they also affect performance, especially when it comes to animations. In this article, we tested different ways to animate shadows and compared their performance. Animating a shadow triggers three events: paint, layout change, and composite. The first event is the most troublesome.
The ideal solution is not to animate the shadow at all (because it looks fine as is!).If you really want to animate box-shadow
Changing only the opacity instead of changing the offset value reduces redraws. The catch is that you lose the illusion of depth that shadows are meant to provide.Another approach is to animate the two. box-shadow
layer. This solution is visually pleasing and performs well, even with extra shadows.
The last option is not animation box-shadow
But it’s a pseudo-element that provides a shadow. This greatly reduces the amount of redraws and the overall work the CPU does when running animations. You write more code, but it’s your best bet to ensure good performance.
Relevant content: