Improving CSS performance and file size - an in-depth guide
Follow this in-depth-guide and learn ways to improve CSS performance and file size.
Table Of Contents
- What makes CSS slow
- File size
- CSS rendering
- Reducing CSS file size
- Minification
- Optimization
- Compression
- CSS structure
- Improving CSS animation performance
- Transitions and animations
- Promoting element into a composite layer
- will-change property
- contain property
- Bonus: CSS Performance tips
What makes CSS slow?
File size
Large file size results in larger download times and parsing times. It's simple as that.
CSS Rendering
In order to understand where CSS rendering bottlenecks may come up, we need to look at the render pipeline steps:
1. Style - Determines which CSS rules apply to which elements in an HTML document.
2. Layout - Determines where the element is positioned on the screen and how much space does it take. CSS properties: position
, top
, left
, right
, bottom
, etc.
3. Paint - Determines visual elements. CSS properties: color
, border
, background-color
, etc.
4. Composite - Draws layers onto the screen. CSS properties: opacity
and transform
.
CSS offers a lot of options in terms of animation and transitions. We can create a transition on a lot of CSS properties. There are transitions and animations which can use GPU and perform really well, resulting in smooth 60 FPS (Frames per second) experience with fluid and smooth animations. On the flipš side, there are CSS properties that, when animated, can result in low FPS and choppy animations.
Any changes or updates happening on one of the steps cause updates on the following steps. We can see that the Layout step is the most expensive to animate and Composite step is the least expensive to animate.
Reducing CSS file size
By decreasing the file size of the CSS file, we can decrease the download time and loading time. Some improvements related to file size can be done even when we are finished working with CSS making it a very effective optimization. For maximum efficiency, it's best to keep in mind and apply these improvements from the start.
Minification
CSS file minification is an easy win and should be the first step for achieving better CSS performance. Minification removes formatting characters inside the CSS file (whitespaces, line breaks, etc.) which are only useful to us (makes the code readable).
/* Non-minified code */
.card {
padding: 1rem;
background-color: #aaa;
}
/* Minified code */
.card{padding:1rem;background-color:#aaa}
There are numerous online, automated and CDN "uglifying" tools that will help you with minification. Tools for CSS minification:
- CSS Compressor - simple online minification
- CSSNano - Automated CSS optimizer + minifier
- Clean CSS - Automated CSS optimizer + minifier
Optimization
Wait... We've already minified our CSS file. Isn't it already optimized?
Well, technically it is. When talking about CSS optimization, we are looking at removing unnecessary CSS or reformatting CSS properties that reduce the number of characters. Unlike minification, this improvement cannot be done on CDN and it must be done when code is being compiled (by a tool like Webpack or Gulp).
Let's take a look at the following example.
/* Unoptimized CSS */
.card {
background-color: #aaaaaa;
padding: 0.5rem 1rem 0.5rem 1rem;
}
.card__content {
padding: 0.5rem 1rem 0.5rem 1rem;
}
At first look, this looks like any regular CSS that anyone would write and not think twice about it. But there are some optimizations that could be done to reduce the number of characters.
/* Optimized CSS */
.card {
background-color:#aaa;
}
.card,
.card__content {
padding:.5rem 1rem;
}
We have reduced the number of characters from 132 to 85 (a 35% reduction). Let's apply minification and see the final result.
/* Optimized CSS with minification */
.card{background-color:#aaa}.card,.card__content{padding:.5rem 1rem}
We have reduced the number of characters from 132 to 68 (48% reduction in total).
You can see how much CSS impact the optimization has. The reduction is a result of removing the DRY code which can be avoided in this simple example. Looking at the CSS file with thousand of lines of code which is being changed on a regular basis (especially if split into several files), we can easily make our code less DRY. Luckily, there are tools that help us optimize our code and keep it DRY.
In the previous section, we have mentioned two of the numerous tools for optimizing CSS with minification included:
Compression
Compression is done server-side. It uses gzip or Brotli to compress and further reduce the file size in order to reduce the file download time.
This topic is very extensive and covers more file types than just CSS. CSS tricks wrote a great article on this topic if you are interested in implementing server-side compression.
It's also important to note that most CDN services offer the compression option out of the box.
CSS structure
Having a good CSS structure and using various CSS best practices and also result in a lower-sized CSS file when minified. For example, using BEM and only class selectors with low specificity will result in fewer characters in the file and the lower file size.
I've covered CSS structure improvements in one of my previous posts. By following these CSS principles, you will have a CSS file with great structure, DRY code and a great foundation for optimization and minification.
Improving CSS animation performance
Transitions and animations
We've covered the CSS rendering pipeline and what makes animation slow. So basically, Layout step is the most expensive to animate and Composite step is the least expensive to animate.
If we look at the CSS properties that cause the Composite layer to update, they are transform
and opacity
. These two properties use hardware (GPU) acceleration, resulting in very performant updates.
If you apply a transition to position attributes like top
or left
, animation performance will be poor because we are updating the Layout step which forces both Paint and Composite to update. Replacing the position CSS properties with transform
and using transition on it, we are updating Composite step only, resulting in smooth animation.
Promoting element into a composite layer
We aren't really restricted to using only transform
and opacity
for smooth animations. CSS offers quite a few ways of "promoting" an element into a composite layer to tell the browser that this element will have its Paint CSS properties updated often.
The hacky and "old way" of doing this is by either adding backface-visibility: hidden;
or transform: translate3d(0,0,0);
. In most cases, these properties won't affect your element in any way (if they don't override anything), but they will make the browser move the element into Composite layer, significantly improving animation performance.
.example {
width: 20rem;
height: 20rem;
background-color: #aaa;
border-radius: 0;
backface-visibility: hidden;
transition: border-radius 0.3s ease-in-out;
}
.example:hover {
border-radius: 50%;
}
will-change property
A modern way of telling browsers which element is expected to be updated (in terms of CSS rendering) is setting the will-change
property.
.card {
background-color: #aaa;
transition: background-color 0.3s ease-in-out;
will-change: background-color;
}
.card:hover {
will-change: background-color;
}
.card:active {
background-color: #bbb;
}
There are several things to keep in mind when using will-change
:
- Don't apply will-change to too many elements.
- Use sparingly
- Don't apply will-change to elements to perform premature optimization.
- Give it sufficient time to work.
contain property
The contain
CSS property represents the future of CSS render pipeline optimization. Currently, it sits at limited browser support. It allows us to indicate that an element’s subtree is independent of the rest of the page. This allows the browser to recalculate layout, style, paint, size, or any combination of them for a limited area of the DOM and not the entire page.
contain: none; /* No optimization */
contain: strict; /* Equivalent to contain: layout paint size style */
contain: content; /* Equivalent to contain: layout paint style */
contain: size; /* component size is set and no descendant will modify its size. */
contain: layout; /* changes to any descendant of this element will not affect the layout of any outside element and vice versa. */
contain: style; /* descendant’s styles will not affect outside elements. */
contain: paint; /* you specify that no descendant will display outside the elements bounds. Similar to overflow: hidden; */
Example of using contain
CSS property:
.box {
contain: layout size; /* Won't change size nor affect layout */
}
<div class="box">
<div class="box__content">...</div>
</div>
Bonus: CSS Performance tips
Avoid using @import
@import
CSS property is used to include a CSS file within another CSS file. The issue is that these files are then loaded one after another instead of parallel when using the recommended HTML way of including stylesheets.
Use HTTP/2 if you have multiple stylesheets
HTTP/2 brings noticeable performance improvements when loading a lot of smaller files. This is an ideal solution if you are using CSS structure that results in multiple CSS stylesheets (individual screens, modules, components, etc.).
If you are not using HTTP/2, it's recommended to merge all your CSS files into a single CSS file.
Be aware of "expensive" properties
Some properties are more resource-heavy and increase render time. If you are intending to support devices with lower processing power, being aware of these properties would be beneficial:
border-radius
box-shadow
filter
position: fixed
*
:nth-child
Serve CSS file(s) over the CDN
Serving static files like images, CSS, JS, etc. from CDN improves download times. Additionally, some CDN services offer basic minification and optimization options, so you can easily get a performance boost if your code is not minified already.
Keep your selectors simple
Having simple class selectors (consider using BEM and OOCSS or other flavors and combinations) can benefit performance when rendering your site, especially on devices with lower processing power. I've covered this topic more in-depth in one of my previous articles:
Thank you for taking the time to read this post. Feel free to share your thoughts about this topic in the comments or drop us an email at hello@prototyp.digital.