Tackling Render Blocking CSS for a Fast Rendering Website

Share

Tackling Render Blocking CSS for a Fast Rendering Website

This article is part of a series created in partnership with SiteGround. Thank you for supporting the partners who make SitePoint possible.

I’ve always thought deciding how and when to load CSS files is something best left to browsers. They handle this sort of thing by design. Why should developers do anything more than slap a link element with rel="stylesheet" and href="style.css" inside the <head> section of the document and forget about it?

Apparently, this may not be enough today, at least if you care about website speed and fast-rendering webpages. Given how both these factors impact on user experience and the success of your website, it’s likely you’ve been looking into ways you can gain some control over the default way in which browsers deal with downloading stylesheets.

In this article, I’m going to touch on what could be wrong with the way browsers load CSS files and discuss possible ways of approaching the problem, which you can try out in your own website.

The Problem: CSS Is Render Blocking

If you’ve ever used Google Page Speed Insights to test website performance, you might have come across a message like this:

Message from Google Page Speed Insights about render blocking CSS.

Here Google Page Insights is stating the problem and offering a strategy to counteract it.

Let’s try to understand the problem a bit better first.

Browsers use the DOM (Document Object Model) and the CSSOM (CSS Object Model) to display web pages. The DOM is a model of the HTML which the browser needs in order to be able to render the web page’s structure and content. The CSSOM is a map of the CSS, which the browser uses to style the web page.

CSS being on the critical rendering path means that the browser stops rendering the web page until all the HTML and style information is downloaded and processed. This explains why both HTML and CSS files are considered to be render blocking. External stylesheets especially involve multiple roundtrips between browser and server. This may cause a delay between the time the HTML has been downloaded and the time the page renders on the screen.

The problem consists in this delay, during which users could be staring at a blank screen for a number of milliseconds. Not the best experience when first landing on a website.

The Concept of Critical CSS

Although HTML is essential to page rendering, otherwise there would be nothing to render, can we say the same about CSS?

Of course, an unstyled web page is not user friendly, and from this point of view it makes sense for the browser to wait until the CSS is downloaded and parsed before rendering the page. On the other hand, you could argue that not all style information is critical to building a usable page. What users are immediately interested in is the above the fold portion of the page, that is, that portion which users can consume without needing to scroll.

This thought is behind the dominant approach available today to solve this problem, including the suggestion contained in the message from Google Page Insights reported above. The bit of interest there reads:

Try to defer or asynchronously load blocking resources, or inline the critical portions of those resources directly in the HTML.

But how do you defer or asynchronously load stylesheets on your website?

Not as straightforward as it might sound. You can’t just toss an async attribute on the link element as if it were a <script>
tag.

Below are a few ways in which developers have tackled the problem.

Take Advantage of Media Types and Media Queries to Minimize Render Blocking

Ilya Grigorik illustrates a simple way of minimizing page rendering block which involves two stages:

  • Split the content of your external CSS into different files according to media type and media queries, thereby avoiding big CSS documents that take longer to download
  • Reference each file with the appropriate media type and media query inside the link element. Doing so prevents some stylesheet files from being render blocking if the conditions set out by the media type or media query are not met.

As an example, references to your external CSS files in the <head> section of your document might look something like this:

<link href="style.css" rel="stylesheet">
<link href="print.css" rel="stylesheet" media="print">
<link href="other.css" rel="stylesheet" media="(min-width: 40em)">

The first link in the snippet above doesn’t use any media type or media query. The browser considers it as matching all cases, therefore it always delays page rendering while fetching and processing the resource.

The second link has a media="print" attribute, which is a way of telling the browser: “Howdy browser, make sure you apply this stylesheet only if the page is being printed”. In the context of the page simply being viewed on the screen, the browser knows that stylesheet is not needed, therefore it won’t block page rendering to download it.

Finally, the third link contains media="(min-width: 40em)", which means that the stylesheet being referenced in the link is render-blocking only if the conditions stated by the media query match, i.e., where the viewport’s width is at least 40em. In all other cases the resource is not render blocking.

Also, take note that the browser will always download all stylesheets, but will give lower priority to non-blocking ones.

To sum up, this approach doesn’t completely eliminate the problem, but can considerably minimize the time browsers block page rendering.

Using the preload Directive on the Link Element

A cutting-edge feature you could leverage to counteract the block of the web page by browsers as they fetch external stylesheets, is the standard-based preload directive.

preload is how we, as developers, can tell browsers to fetch specific resources because we know the page is going to need those resources quite soon.

Scott Jehl, designer and developer working for The Filament Group, can be credited to be the first one who tinkered with this idea.

Here’s what you need to do.

Add preload to the link referencing the stylesheet, add a tiny bit of JavaScript magic using the link’s onload event, and the result is like having an asynchronous loading of the stylesheet directly in your markup:

<link rel="preload" as="style" href="style.css" onload="this.rel='stylesheet'">

The snippet above gets the browser to fetch style.css in a non-blocking way. After the resource has finished loading, i.e., the link’s onload event has fired, the script replaces the preload value of the rel attribute with stylesheet and the styles are applied to the page.

At this time browser support for preload is confined to Chrome and Opera. But fear not, a polyfill provided by the Filament Group is at hand.

More on this in the next section.

The Filament Group’s Solution: From Inlining CSS to HTTP/2 Server Push

Solving the problem of delayed page rendering due to fetching of critical resources has long been the highest priority for the Filament Group, an award-winning design and development agency based in Boston:

Our highest priority in the delivery process is to serve HTML that’s ready to begin rendering immediately upon arrival in the browser.

The latest update on optimizing web page delivery outlines their progress from inlining critical CSS to super efficient HTTP/2 Server Push.

The overall strategy looks like this:

  1. Any assets that are critical to rendering the first screenful of our page should be included in the HTML response from the server.
  2. Any assets that are not critical to that first rendering should be delivered in a non-blocking, asynchronous manner.

Before HTTP/2 Server Push, implementing #1 consisted in selecting the critical CSS rules, i.e., those needed for default styles and for a seamless rendering of the above the fold portion of the webpage, and cramming them in a <style> element inside the page’s <head> section. Filament Group devs devised Critical, a cool JavaScript plugin, to automate the task of extracting critical CSS.

With recent adoption of HTTP/2 Server Push, Filament Group has moved beyond inlining critical CSS.

What is Server Push? What is it good for?

Server push allows you to send site assets to the user before they’ve even asked for them. …

Server push lets the server preemptively “push” website assets to the client without the user having explicitly asked for them. When used with care, we can send what we know the user is going to need for the page they’re requesting.

Jeremy Wagner

In other words, when the browser requests a particular page, let’s say index.html, all the assets that page depends upon will be included in the response, e.g., style.css, main.js, etc., with a huge optimization boost on page rendering speed.

Putting into practice #2, asynchronous loading of non critical CSS, is done using a combination of the preload directive technique discussed in the previous section and LoadCSS, a Filament Group’s JavaScript library that includes a polyfill for those browsers that don’t support preload.

Using LoadCSS involves adding this snippet to your document’s <head> section:

<link rel="preload" href="mystylesheet.css" as="style" onload="this.rel='stylesheet'">
<!-- for browsers without JavaScript enabled -->
<noscript><link rel="stylesheet" href="mystylesheet.css">
</noscript>

Notice the <noscript> block that references the stylesheet file as usual to cater for those situations when JavaScript goes wrong or has been disabled.

Below the <noscript> tag, include LoadCSS and the LoadCSS rel=preload polyfill script inside a <script> tag:

<script>
/*! loadCSS. [c]2017 Filament Group, Inc. MIT License */
(function(){ ... }());
/*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */
(function(){ ... }());
</script>

You can find detailed instructions on using LoadCSS in the project’s GitHub repo and see it in action in this live demo.

I lean towards this approach: it’s got a progressive enhancement outlook, it’s clean and developer friendly.

Placing the Link to the Stylesheet Files in the Document Body

According to Jake Archibald, one drawback of the previous approach is that it relies on a major assumption about what you or I can consider as being critical CSS. It should be obvious that all styles applied to the above the fold portion of the page are critical. However, what exactly constitutes above the fold is all but obvious because changes with viewport dimensions affect the amount of content users can access without scrolling. Therefore, one-size-fits-all is not always a reliable guess.

Jake Archibald proposes a different solution, which looks like this:

<head>
</head>
<body>
<!-- HTTP/2 push this resource, or inline it -->
<link rel="stylesheet" href="/site-header.css">
<header></header>

<link rel="stylesheet" href="/article.css">
<main></main>

<link rel="stylesheet" href="/comment.css">
<section class="comments"></section>

<link rel="stylesheet" href="/about-me.css">
<section class="about-me"></section>

<link rel="stylesheet" href="/site-footer.css">
<footer></footer>
</body>

As you can see, references to external stylesheets are not placed in the <head> section but inside the document’s <body>.

Further, the only styles that are rendered immediately with the page, either inlined or using HTTP/2 Server Push, are those that apply to the upper portion of the page. All other external stylesheets are placed just before the chunk of HTML content to which they apply: article.css before <main>, site-footer.css before <footer>, etc.

Here’s what this technique aims to achieve:

The plan is for each <link rel=”stylesheet”/> to block rendering of subsequent content while the stylesheet loads, but allow the rendering of content before it. The stylesheets load in parallel, but they apply in series.

… This gives you a sequential render of the page. You don’t need decide what’s “above the fold”, just include a page component’s CSS just before the first instance of the component.

All browsers seem to allow the placement of <link> tags in the document’s <body>, although not all of them behave exactly in the same way. That said, for the most part this technique achieves its goal.

You can find out all the details on Jake Archibald’s post and check out the result in this live demo.

Conclusion

The problem with the way in which browsers download stylesheets is that page rendering is blocked until all files have been fetched. This means user experience when first landing on a web page might suffer, especially for those on flaky internet connections.

Here I’ve presented a few approaches that aim to solve this problem. I’m not sure if small websites with relatively little and well-structured CSS would be much worse off if they just left the browser to its own devices when downloading stylesheets.

What do you think, does lazy loading stylesheets, or their sequential application (Jake Archibald’s proposal), work as a way of improving page rendering speed on your website? What is the technique you find most effective?

Hit the comment box below to share