Part 2 of Blue Triangle’s Accelerator Series: Best Practices to Improve Web Performance
When you start optimizing resources that load on your website, CSS might not be the first thing that you think of, but CSS affects everything that renders.
CSS – which stands for “cascading style sheets” – formats the page you see in a browser, from position of elements to color, to click-ability. In other words, CSS is critical for user experience.
How can CSS slow down your website?
Just like unoptimized images, unoptimized CSS can lead to slower pages – and lower conversion rates. Notice how conversion rates drop as the page takes longer to load:
Performance matters, so don't let unoptimized CSS slow down your site. Here's how CSS can slow you down:
- CSS can stop the parsing of HTML – While CSS parses, it can block other resources from loading, including JS functionality. In fact, you probably won’t be able to interact with the page at all.
- CSS can render block – The first pixel of a page won’t appear until all CSS is parsed.
CSS is critical to render the page, and as a result, it’s part of the critical rendering path – the minimum amount of information that a webpage needs to display the first pixel of the page.
HTML and CSS are the two minimum requirements for the critical rendering path. HTML is the document that builds the page and parsing it constructs the document object model (DOM). CSS styles the DOM via the CSS object model (CSSOM). For CSS to be used by the page, it has to load and then parse.
Today, I’ll be focusing on best practices for loading (not parsing) CSS since the biggest web performance improvements from CSS optimization are found in the efficient delivery of it.
To optimize the delivery of CSS and speed up your site, you should:
- Reduce the file size of CSS and
- Use network features that help CSS download faster and earlier.
Let’s explore how.
Reduce CSS file size
Efficient delivery of CSS files means limiting the amount of data that needs to be transferred and being clever about the means of delivery.
There are three basic optimizations you can make to limit the amount of data transferred in the delivery process:
- CSS minification,
- CSS file length reduction by reducing unnecessary code, and
- CSS compression to deliver a smaller file that the browser can still read.
Minify CSS
Minification removes all whitespace, comments, and extraneous characters in CSS. It would turn this (48 characters):
h1 {
color: black;
background-color: blue;
}
into this (38 characters):
h1{color:black;background-color:blue;}
Here are some tools to minify your CSS:
- For manual minification, you can use CSSNano or csso.
- Larger sites that can benefit from automated workflows can use Gulp or Webpack.
Of course, minification can make code hard to read, so if you’re stuck with the minified code you need to debug, you can use a beautification tool like Unminify.
And if you’re using a framework with a CSS library, like Bootstrap, Foundation, or Susy, make sure you use compilers and minified files to keep from loading in unused CSS styles, which only bloat the code.
Reduce CSS file length
Using shorthand and eliminating redundancy is related to minification because some minification tools will implement shorthand for you. Take this hexadecimal color style:
p {
color: #00000;
}
Some minification tools will turn that into the shorthand hexadecimal:
p {
color: #000;
}
There are other shorthand properties you can use that reduce the amount of code needed. And when styles overlap in CSS, there is no need to rewrite the styles. This would be a proper implementation of the syntax:
h1, h2 {
color: blue;
}
Not this:
h1 {
color: blue;
}
h2 {
color: blue;
}
Limiting style definitions also apply to inheritance – some properties in CSS are inherited in HTML by nested elements (called descendants). You can also force inheritance in property definitions when inheritance isn’t the default. Inheritance means that this:
<div style = “background-color: blue;”>
<h1 style = “background-color: blue;”>A Heading</h1>
<p style = “background-color: blue;”>A paragraph.</p>
</div>
is this same as writing this:
<div style = “background-color: blue;”>
<h1>A Heading</h1>
<p>A paragraph</p>
</div>
Inheritance applies to CSS regardless of where it appears – inline, embedded, or in an external style sheet. Use CSS syntax to limit the number of style definitions in the CSS document. The bonus is that it’s usually easier to read.
One last note – you may have a CSS code base that’s older and unwieldy. To see which styles aren’t being used in your CSS, you can use a tool like unCSS. You may be able to reduce your file length even further.
Compress CSS
Compression is done server-side and reduces the number of bytes in the CSS file without changing the contents.
Most modern browsers can read compressed files, so when a browser requests a resource from the server, it will let the server know if it can read the compressed file. If available, the compressed file will be delivered to the browser. Otherwise, the server delivers the full (uncompressed) file.
Uncompressed files can be much larger than compressed files, so this kind of optimization is a no-brainer. If you’re not using compression, chances are your site will be slower than competitors.
Download CSS faster and earlier
Remember that the earlier your styles load, the faster the page can render. There are several ways to manipulate how styles load on your site. To work the delivery system, you can take advantage of two delivery methods that each have two parts:
- HTTP preload and push, which allow you to load CSS earlier than usual, and
- lazy loading combined with embedding CSS into HTML.
Both of these delivery methods allow HTML to parse while the CSS loads.
Deliver CSS with HTTP/2 push or HTTP preload
HTTP/2 push and HTTP preload are two mechanisms that manipulate network behavior, in slightly different ways.
HTTP preload
Using preload, a newer feature in HTTP, allows styles to load earlier because the browser requests them earlier, as soon as HTML parsing begins. Preloading can be denoted in markup, or in the HTTP header.
Preloading in an HTML document:
<link rel="preload" href="/styles/other.css" as="style">
Preloading in an HTML response header:
Link: <https://example.com/styles/other.css>; rel=preload; as=style
Preloading doesn’t produce blocking behavior either, and it can be used for third-party content, which allows you to control the way the resource gets used. For example, you could fetch a stylesheet for a third-party component that isn’t necessary for the initial rendering of the page, but that will be used a little later on in the page load.
HTTP/2 push
HTTP/2 push is a little different. In effect, they accomplish many of the same goals, and preload actually uses the push API when it’s available by default. To prevent this, you can include “nopush” in the HTTP response header, like this:
Link: </styles/other.css>; rel=preload; as=style; nopush
When HTTP/2 push gets used, it’s the server doing the pushing, rather than the browser in preload. This important distinction means that the CSS file getting pushed immediately transfers with the HTML and other essential files using push. Push can load resources even faster than preload as a result. If you use push, though, you can risk overriding the use of cached resources and increasing the amount of data sent, so be careful with implementation.
Another important distinction is that push can only be used for HTTP/2 connections. If your site doesn’t have HTTP/2 enabled, it will not be able to use push. This also means that the way you initiate a push depends on your server configuration.
Special considerations
Neither preload nor push can be used universally. Still, it’s a good idea to use them, because browsers that can’t use preload or push will ignore these attributes and browsers that can use them stand to load pages much faster. Preload is supported by most major browsers, but without support in Firefox, Opera, and IE, and only partial support in Edge. On the other hand, Firefox supports push, but Safari and IE don’t.
Embed and lazy load CSS
When preload and server push aren’t available, you can embed CSS inside a <style>
tag in the HTML.
Embedding all of your CSS can get a little hairy though – you’re increasing the file size of the HTML, and CSS can’t be cached for other page visits if you load it this way. But there’s a workaround:
- Embed only the CSS that styles above-the-fold content.
- Lazy load the rest.
Embedding CSS in a <style>
tag doesn’t stop HTML parsing. Lazy-loading the non-critical CSS allows it to load in asynchronously, which also allows the HTML to parse. As a result, the page renders faster.
In reality, determining what content is above the fold is very difficult, because it largely depends on how that content is styled. There are a couple of projects online to help you determine which CSS styles are necessary to include for above-the-fold content, like this CriticalCSS bookmarklet from Paul Kinlan. For loading the uncritical content asynchronously, loadCSS is probably the best-known project.
Another consideration is caching here – embedding CSS in HTML doesn’t allow CSS to be cached for future page visits. For that reason, you may consider lazy loading the full CSS file at a specific point in the page so that it gets cached. Additionally, you might only need to follow this pattern on the main landing pages of your site to avoid downloading a larger HTML file on multiple pages.
Takeaways and the TL;DR
Optimizing CSS is multilayered. You have to write performant code, make sure it’s as small as possible, and deliver it effectively to your end users. The way to do that is understanding how CSS works in relation to the rest of a webpage and with respect to the browser.
Because there are so many facets to CSS optimization, always test changes before you put them into production. If aesthetics, usability, or functionality are impacted by your CSS changes, run an A/B test.
And here’s the TL;DR for delivering optimized CSS for better web performance:
- Minify CSS to reduce file size
- Use shorthand and inheritance to limit the code needed
- Get rid of unused CSS properties in old code
- Compress CSS server-side to use the same CSS with less data
- Preload and server push to deliver it faster
- Embed above-the-fold styles and lazy load non-critical CSS when preload and push aren’t available
During the holiday rush, every shopper matters
Optimize the customer journey before the eCommerce event of the year.