CSS and the critical path

June 5th, 2012. Tagged: CSS, performance

Back when I was still actively into speaking at public events (way, way back, something like year and a half ago (which strangely roughly coincides with the time I joined Facebook, hmmm (hmm? (huh? what's with the parentheses? sure all of them are closed at this point?)))) I remember showing this slide:

The reason I'm bringing it up now is this experiment I saw today by Scott Jehl.

media="nonsense"

Scott added LINK elements with non-applicable media attribs, such as tv, too much min-width and pixel ratio of 6 among others:

<link href="inc/tv.css" rel="stylesheet" media="tv">
<link href="inc/min-width-4000px.css" rel="stylesheet" media="(min-width: 4000px)">
<link href="inc/min-device-pixel-ratio-6.css" rel="stylesheet" 
    media="(min-device-pixel-ratio: 6)">

And just for the fun of it, why not a nonsense value:

<link href="inc/nonsense.css" rel="stylesheet" media="nonsense">

Scott observed that (with one happy Opera nonsense exception) all browsers will load all this junk, all this CSS that they don't need.

(BTW, Opera 11.64 loaded nonsense css for me too)

Blocking rendering?

Having recently remembered how browsers block rendering because of print stylesheets, I speculated that all the nonsense media will also block rendering. Unfortunately I was right.

So not only browsers download useless bytes, but they also block the rendering of the page (or block window.onload, or both) until all the crap is downloaded. And by blocked rendering I mean showing a white page of death. Most browsers wait until all CSS is loaded because they don't like doing extra layouts and painting (except Opera).

Here's a test page for you to try:

http://www.phpied.com/files/css-loading/mq.php?mq=all

Change all with your media query of choice, hit Enter and weep.

E.g.
http://www.phpied.com/files/css-loading/mq.php?mq=tv
http://www.phpied.com/files/css-loading/mq.php?mq=nonsense

This test page loads css with delay: css1 delayed 5 seconds and css2 delayed 10 seconds. The HTML is:

<link rel="stylesheet" href="css1.css.php" type="text/css" media="screen" />
<link rel="stylesheet" href="css2.css.php" type="text/css" 
    media="<?php echo $YOUR_MEDIA_QUERY; ?>" />

The correct browser behavior should be:
1. load only the CSS you need
2. render
3. fire onload

Maybe even:
0. render if step 1. takes too long

Instead, randomness ensues: Firefox treats us to a white page for 10 seconds while downloading nonsense. Chrome takes 15 seconds to fire onload. (see the print CSS post for more)

So what are we to do? First, understand...

The evil that CSS do

  1. Browsers (except Opera) block rendering until all screen CSS arrives. With the worst possible experience: white page.
  2. Browsers download CSS they don't need, e.g. print, tv, device-ratio... And most browsers (except Opera and Webkit) block rendering because of these too
  3. Sometimes CSS blocks the other downloads too (not just block rendering, but block images and scripts that follow):

The critical path

When building high-performance pages we want to stay off the critical path. Critical is the path from the user following a link to the first impression and then the working experience. That's why we load javascript asynchronously and so on.

But I argue that CSS is not only on the critical path, it is the critical path. And because it's a jungle (network, 3g, edge) out there, anything on the critical path will fail. Guaranteed.

Think about this: you have an HTML page and then you have components. Without the HTML, there is no path really. Game over. Without images? Depends on the page, but you can live without images most of the time. Without JavaScript? Well you should build the pages so the important stuff, links, forms, content works without javascript. Without webfonts? You're kidding me, I don't need no stinkin' fonts when I'm late and running to the airport and checking in for the flight on the damn phone with the spotty mobile network while Wifi wants to connect and I have to say no, because if I say yes I'll wait for another page where I have to say "I accept" and aim at a miniscule checkbox with these sweaty fat fingers or worse I have to enter usernameandpassword, and omg-omg-OMG mobile.southwest.com wants to look like native iPhone and won't let me click until mountains of JS arrive, so no, don't talk to me about no damn fonts!

What's left on the critical path is CSS. Not only the page is ugly without CSS, we can live with that, but there is no page without CSS because the browser waits and waits and takes forever to timeout showing us a blank white page.

Get the CSS out of the way

So if you worry about performance, you should get the CSS out of the way as soon as possible. Get off the critical path. Make CSS small, minify, compress, load from the same hostname even (no DNS) and inline, if small enough. Yup, inline.

Take a look at these highly optimized experiences...

Look ma, no CSS!

Yes, these pages make no CSS requests whatsoever.

If your CSS is not puny enough to be all inline (Guy has some observations on what puny means) it should at least be a single file, way at the top of the document, with the first flush. Just get it over with. Your users will love you and praise you and use words like smooth and snappy.

Tell your friends about this post: Facebook, Twitter, Google+

Sorry, comments disabled and hidden due to excessive spam. Working on restoring the existing comments...

Meanwhile, hit me up on twitter @stoyanstefanov