Eliminate render-blocking CSS in above-the-fold content

April 25, 2018 0 Comments

Eliminate render-blocking CSS in above-the-fold content

 

 

It all started here. I was convinced I was doing at least okay, but Google’s PageSpeed Insights ranked me an unequivocal “Low”.

Tavolia is the largest project I’ve ever worked on, so it makes sense it would be the first where I would encounter actual speed issues.

I know Google is not an absolute source of truth, but they are the dominant search engine, and making them happy means better SEO.

I want the site to rank well on SEO, and I want it to load ridiculously fast for my users, especially on mobile phones.

Luckily, Google tells me how exactly I should improve the homepage’s performance :

I’ll be learning how to fix these while doing them at the same time, so let’s break it down ! Today we’re looking at step one : render-blocking CSS.

There are three ways to load CSS :

Loading CSS before the body will ensure the content loads with styles already applied. Pro : my content won’t move around while loading. Con : nothing will show up while the CSS isn’t done loading.

Loading CSS after the body will make your content load much faster, because all the HTML can load ahead of time. Pro: content shows up earlier. Con : the content isn’t styled at first. This is what I’m doing now.

Inlining above-the-fold CSS will make my content and styles load very fast for everything that’s at the top of my home page. Pro : content and styles both load very fast, best user experience. Con : needs more work, harder to maintain. It’s what we’ll be doing today.

Here is what google is telling me precisely about my render-blocking things :

Note : there is no JS involved, I’ve already placed all of that at the bottom of my page.

Here is my home page when I remove each of the three files Google is pointing at :

The three css files Google is pointing at are :

  • Bootstrap.css, which I use for a lot of little things like dropdowns and tables. It’s a big file (137kb, 19kb gzipped) with a lot of unused rules. Unsurprisingly, it doesn’t affect the result much, only breaking the top dropdowns and slightly changing my title and subtitle’s font weights.
  • Meta.css, which governs most of the CSS on my site. Here it’s mostly changing the look of my navbar and the color of the primary button.
  • Home.css, built specifically for this page, is the main meat of this above-the-fold content. Without it, the whole structure breaks down.

An easy win could be to inline the entire “Home.css” file. Since it’s specific to this page, the css will only be in one place, which will make it easier to maintain.

Let’s see what the page looks like with only “Home.css”, compared to the expected final result :

It’s a good start and a decent first paint, but if I leave it at that, the content will bump around until it reaches the end result. This won’t be a great user experience, and Google will point out that my above-the-fold content changes too much.

So let’s see what further improvements can be made.

Bootstrap’s reset module removes the default margins that are applied to <body> tags. I’ll mimic that so the margins are gone from the start. I’ll do the same with the border-box rule.

I’ll also copy the body’s background-color and font-family so that the page has the proper light background color and font stack right from the start. I’m using a native font stack, so there won’t be any extra font-loading times.

I’ll apply my font-size rule to the <html> tag as well, so sizing is consistent.

Finally, I’m copying bootstrap’s line-height and font-weight attributes so the title and subtitle don’t change height and weight while loading.

html {
font-size: var(--base-font-size);
}
body {
margin: 0;
background-color: var(--primary-background-color);
font-family: var(--body-font);
}
, ::after, ::before {
box-sizing: border-box;
}
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
line-height: 1.2;
font-weight: 500;
}

Note : since I’m relying heavily on CSS variables, this also means that all my CSS variables are now inlined.

Here’s the result :

We’re getting somewhere ! Now to deal with that pesky navbar

When dropdown buttons are clicked, the dropdown menu gets a “show” class and the following rule applies :

.dropdown-menu.show { display: block; }

So I can just go ahead and hide the dropdown menus right from my inline styles :

.dropdown-menu { display: none; }

I’ll also hide all of the contents of my navbar until their CSS is loaded. Luckily, all my navbar’s links are wrapped in a .nav-button-wrapper class, so I can target them easily.

Also, I want my empty navbar to have the correct height, so I’ll give it some extra padding that will be removed after the rest of the CSS is loaded.

Finally, my navbar has a thick purple top border, and a dashed bottom border. I’ll copy that rule from my meta.css to the inline styles. This will repeat the code and make it harder to maintain, but I’m taking the chance.

Here is the recap regarding my navbar :

.dropdown-menu { display: none; }
.nav-button-wrapper { display: none; }
nav {
padding: 1.4rem;
border-top: .25rem solid var(--primary-color);
border-bottom: 1px dashed var(--tenth-primary-color);
}

And in my Meta.css :

/ Cancelling out above-the-fold css from home-css.html /
.nav-button-wrapper { display: block; }
nav { padding: 0; }
Final result loading smoothly

So this is where I am right now :

Navbar correctly doesn’t show up yet, but now we’ve got to fix those bottom buttons !

I’m partly using bootstrap’s button classes with .btn-primary and btn-light, but there’s also quite a bit of custom code.

I’ll remove the underline, change the color, add some padding for both buttons.

The primary button’s texture will take time to load, so I’ll replace it with an inlined color placeholder.

The light button will get its border and font-size inlined as well.

And to make things align properly, I’ll need to set the buttons to inline-block, and give their wrapping paragraphs the default bootstrap margins.

p { margin-top: 0; margin-bottom: 1rem; }
.btn {
display: inline-block;
padding: .5rem 1rem;
border-radius: 0;
text-decoration: none;
}
.btn-primary {
background-color: #d84a41;
border: 0;
color: white;
}
.btn-light {
font-size: .875rem;
border: 1px solid #ccc;
color: inherit;
}

And finally, here is the end result :

On the left with only inlined css, on the right, when everything is finished loading

Google loves me now.

59/100 to 89/100, pretty strong improvement !

To be fair, I didn’t think these changes would have such an impact on the score. They don’t affect user experience *that much and I thought the weight of my high server response time played a bigger part.

But hey, I’m not complaining !


Tag cloud