DEV Community

Cover image for Improving CSS performance and file size - an in-depth guide
Adrian Bece for PROTOTYP

Posted on • Updated on • Originally published at blog.prototyp.digital

Improving CSS performance and file size - an in-depth guide

Table Of Contents

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.

The full pixel pipeline

The full pixel pipeline (from Google Developers site)

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}
Enter fullscreen mode Exit fullscreen mode

There are numerous online, automated and CDN "uglifying" tools that will help you with minification. Tools for CSS minification:

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;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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}
Enter fullscreen mode Exit fullscreen mode

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:

  • CSSNano - Automated CSS optimizer + minifier
  • Clean CSS - Automated CSS optimizer + minifier

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%;
}

Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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; */
Enter fullscreen mode Exit fullscreen mode

Example of using contain CSS property:

.box {
  contain: layout size; /* Won't change size and won't affect layout */
}
Enter fullscreen mode Exit fullscreen mode

<div class="box">
  <div class="box__content">...</div>
</div>

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:


These articles are fueled by coffee. So if you enjoy my work and found it useful, consider buying me a coffee! I would really appreciate it.

Buy Me A Coffee

Thank you for taking the time to read this post. If you've found this useful, please give it a ❤️ or 🦄, share and comment.

Top comments (14)

Collapse
 
moopet profile image
Ben Sinclair

I'm dubious about tips relating to decreasing the transfer size for CSS assets.

They're loaded once, and cached. Or they're served from a CDN. Once they're loaded they're unpacked in milliseconds where whitespace makes no difference and the difference between short-forms and full forms is inconsequential unless you have millions of rules.

What that means is maybe a dozen milliseconds more load time for the first page and then no perceivable effect on subsequent pages.

Keeping selectors simple is more worthwhile, but still most people won't notice the difference, especially when you consider that your CSS is likely aggregated with that provided by the five hundred "essential" plugins your framework bundles.

Collapse
 
adrianbdesigns profile image
Adrian Bece

Speaking from experience working on an massive eCommerce site, minifying, concatenating and optimizing 2MB CSS file to 350kB-ish file resulted in an increase of mobile users. The improvement also added lazy loading for images and minifying JS and it boosted the number of mobile users significantly.

The problem was that users on mobile and slower connections would perceive the site as slow and unresponsive due to the massive file being loaded.

It's true that everything is well after that first load, but users might give up if they wait too long for that first load.

Collapse
 
elmuerte profile image
Michiel Hendriks

If you can play the Doom shareware episode in the size of your CSS you are doing something really wrong.

Thread Thread
 
adrianbdesigns profile image
Adrian Bece

I've done quite a few audits of various sites in my career. I have seen some really crazy stuff I haven't thought of possible. Including the CSS and JS files with source maps included.

Collapse
 
dimitrovski_a profile image
Antonio Dimitrovski

You are wrong, compressing the CSS speed up the website quite a lot.
After caching the website is fast but big BUT the first load it's most important the first load is the load that make sales, make great user experience etc...

Slowly 2019 is coming to end, every again every website should load critical CSS per URL and load the rest 99% of the CSS after the page as non render blocking.

Me as SEO Manager, I can't hire someone to optimize CSS if the developer don't know what critical CSS is and how to do it.

It was hard to find a developer who know how to do technical SEO optimization per instruction so if the website is WordPress I'm doing it myself but can't do it at custom coded website. Please educate yourself for SEOs needs because nearly every website is searching for SEO services.

Every one will ask you as developer to optimize the website as speed is ranking factor now.

We can extend further this conversation with examples and I can show you the difference white space is doing or speaking about discarding invalid CSS properties other than CSS 3.

Let's extend this conversation I might be able to find someone reliable that I can hire in the future.

Collapse
 
benstigsen profile image
Benjamin Stigsen • Edited

The class / ID names in the HTML / JS could also be minified / replaced with something shorter, allowing one to minify the class / ID specific CSS names.

I've never use that many slashes in a sentence before

Collapse
 
bayuangora profile image
Bayu Angora

I have this code ->

.entry {
background: #fff;
transition: transform 0.5s ease, background 0.5s ease;
}
.entry:hover {
transform: scale(1.02);
}
.darkmode .entry {
background: #444;
}

Is that efficient?

Collapse
 
simonholdorf profile image
Simon Holdorf

I like your Posts Adrian, good Job!

Collapse
 
adrianbdesigns profile image
Adrian Bece

Thank you very much, Simon.

Dev.to and the community really inspired me to write articles more often and put more work into them.

Collapse
 
peke314 profile image
Victor Janin

Great article! Thank you for taking the time to write this, very insightful

Collapse
 
adrianbdesigns profile image
Adrian Bece

Thank you very much. Glad you like it!

Collapse
 
ryanpwalker profile image
Ryan Walker

Correct me if I'm wrong but http/2 is fine if all of the stylesheets come from the same server, otherwise multiple http requests slows down mobile.

Collapse
 
alidhuniya profile image
Ali Hussain Dhuniya

A very helpful and informative post. It's good to animate position, scale, rotate and opacity.
for more: html5rocks.com/en/tutorials/speed/...

Collapse
 
adrianbdesigns profile image
Adrian Bece

Thank you. I do remember reading this article quite a while back.