We’re obsessed with speed – how to make websites load faster. As they often say about cars “you can go as fast as you want – how much do you want to spend?” In that way, cars and web design are similar – speed costs time and money. We’ve found that one of the best ways to increase a website’s speed for an affordable cost is implementing Critical CSS. We do it for all custom web design projects. A lot of people don’t know about Critical CSS, though – it’s a bit of a mystery. Let’s get into what it is and how to use it so you can speed up your website’s loading time and PageSpeed Score. As a side benefit, you’ll turn coffee into beer. Promise.

What is Critical CSS?

If you’ve tested a website’s speed and gotten a message like Eliminate render-blocking JavaScript and CSS in above-the-fold content or Eliminate render blocking CSS then you know what Critical CSS isn’t. The full message usually reads something like

Your page has 2 blocking CSS resources. This causes a delay in rendering your page.

None of the above-the-fold content on your page could be rendered without waiting for the following resources to load. Try to defer or asynchronously load blocking resources, or inline the critical portions of those resources directly in the HTML.

Optimize CSS Delivery of the following:

https://yourdomain.com/some/path/to/some/file.css
https://yourdomain.com/some/path/to/some/other/file.css

And when you follow that Optimize CSS Delivery link, you get some helpful information from Google like

Before the browser can render content it must process all the style and layout information for the current page. As a result, the browser will block rendering until external stylesheets are downloaded and processed, which may require multiple roundtrips and delay the time to first render. See render-tree construction, layout, and paint to learn more about the critical rendering path, and render blocking CSS for tips on how to unblock rendering and improve CSS delivery.

But then it gets into a lot of pretty heavy stuff, which gets kind of depressing. So you go get some coffee and probably a snack and maybe put on that new N.E.R.D. album (Lemon is pretty hot) and try to forget about it all. Been there.

It doesn’t have to be depressing. Let’s break it down. Google is saying what is true. A browser has to load up and parse all of the markup (HTML) and styling (CSS) before it can render a page. It also has to do the same for all the Javascript, though that’s a subject for another discussion. View source on your page and go from top to bottom and open all those CSS stylesheets. You won’t get very far before you go get more coffee. By the time you get back, the browser is probably still not showing anything because it’s busy going through all those stylesheets. All joking aside, there’s gotta be a better way.

This is where Critical CSS comes in – take all that CSS, strip out only what is needed to show the above-the-fold content, put that CSS directly into the <head> section, defer the rest of the CSS into the footer of the page with Filament Group’s loadCSS or by outright moving the <link rel="stylesheet" type="text/css" href="path/to/your/css/style.css"> right into the bottom of the page (argh, I know, CSS isn’t supposed to go there), and then go get a beer because you just sped everything up and got rid of those Eliminate render blocking CSS messages. Drink that beer and send your client a nice invoice.

Why Use Critical CSS?

Because speed. Because awesome. Because you can. But really, it’s important. So here’s how you justify the beer you just drank and the invoice you just sent. There’s a zillion online statistics from famous people like Amazon and Intuit and Google and whatnot that can tell you how much page speed affects everything. The truth is, you’re reading this because you want to figure out how to eliminate render blocking CSS and speed up your website. So let’s get on with it.

Above the fold is a super ancient graphic design term that comes from newspaper printing days. It refers to the part of the newspaper that was visible above the part that got folded in half. Paper – remember that?

What is Above The Fold?

Above the fold is all the content a viewer sees before scrolling. In other words, it is the most important part of a website – the stuff an impatient viewer is sitting there (still?) waiting for. There is no universally defined pixel height of what is considered above the fold content – the web is fluid and there are many devices out there like your phone, your fridge, your new car.

External CSS – any CSS loaded up via an external stylesheet – that is required to render this top section of the page is considered render blocking because it does just that – blocks the rendering of the page until it can be loaded up and parsed.

Critical CSS is kind of like the opposite of render blocking CSS – the minimum, most critical part of the styling needed to show the top of a page. It usually contains all the styling for the grid framework, the navigation and basic font styling. In order to generate Critcial CSS, you will need to pick a point on the page above which you consider the CSS to be Critical. That point is arbitrary and up to you. We use a safe number around 1,300 pixels.

If you want to know exactly what *Google* thinks is above the fold, insert viewportsizes.com/mine/ into Google’s PageSpeed Insights and squint to see the results

This is all great, but how do I generate and add Critical CSS?

Okay – enough beating around the bush. Let’s get to business. Here’s how to do it.

The best free resource for generating Critical CSS is the awesome Critical Path CSS Generator by Jonas Ohlsson Aden. You’ll see more about him later, as he’s taken this great resource to even better places.

  1. Paste the url for your page
  2. Copy and paste *all* the css for your page – even the Bootstrap or Foundation framework styling
  3. Sit back and enjoy that beer
  4. Copy the generated Critical CSS into a <style> tag way up high in the <head> of your page
  5. Move that <link rel="stylesheet" type="text/css" href="../your/style.css"> down to the bottom of your page above the </body> tag, down with all the <script>...</script> tags
  6. Load up that new Critical CSS powered page in Google’s PageSpeed Insights and rejoice at not seeing those peksy Eliminate render-blocking CSS in above-the-fold content messages
  7. Sit back and enjoy that beer

Your minified Critical CSS in a <style> tag in the <head> should look something like this (with a whole lot more CSS)

<!DOCTYPE html>
<html lang="en-US" prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#" itemscope itemtype="https://schema.org/LocalBusiness">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <style>@charset "UTF-8";*,::after,::before{-webkit-box-sizing:border-box;box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;
    nav{display:block}body{margin:0;font-family:proxima-nova,"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:1rem;font-weight:300;line-height:1.5;color:#222;text-align:left;background-color:#fff}</style>
    <meta name="mobile-web-app-capable" content="yes">

Challenges

If you’re hand coding websites, then you’re off to the races. Follow the above steps, rinse and repeat. You’re done.

Most people aren’t hand coding websites. You’re probably using some kind of Content Management System like WordPress. In that case, you have a couple problems to deal with

  1. You’re dealing with a lot of pages
  2. You can’t efficiently add different Critical CSS to every page
  3. Your website may load a ton of different CSS stylesheets

Solutions

Here’s how we deal with these challenges

  1. You’re dealing with a lot of pages – Lots of pages get generated from some form of templating. Focus on breaking out your templates into groups and figure out what pages could use the same Critical CSS
  2. You can’t efficiently add different Critical CSS to every page – Computers are good at batching. Let the machines do the work
  3. Your website may load a ton of different CSS stylesheets – Try not to do this. It slows down your website and that’s bad for business. If you’re using a Sass workflow, combine all these CSS files into one. Then you have only one main CSS file to pull your Critical CSS out of and only one CSS file to move to the bottom of the page

Our Workflow … Gulp + Penthouse = Kapow!

We use Gulp – a JavaScript toolkit – to automate a lot of the pesky tasks involved with web development. Generating Critical CSS over and over each time there’s a style change that affects above the fold content can be annoying to say the least. So Gulp is an ideal tool to automate the process.

To actually generate the Critical CSS, we initially used Filament Group’s CriticalCSS generator, which is awesome. I’m not a historian but Filament Group seemed to be one of the first on the scene. However the repository isn’t updated very often.

Jonas Ohlsson Aden (remember him, he developed the awesome + free Critical Path CSS Generator) pointed us towards the Critical CSS generator Penthouse that he’s the creator of. It’s faster more frequently updated. We switched over to use Penthouse to do the heavy lifting of generating the Critical CSS. Penthouse has a ton of great documentation with examples from simple implementation to batching up lots of pages at a time.

This setup has been working like a gem to do lots and lots of Critical CSS generating:

  1. Add Penthouse via npm to the package.json dependencies:
    "dependencies": {
        "penthouse": "^1.4.0"
      }
    
  2. Add the following to the gulpfile.js requirements:
    var penthouse = require("penthouse");
    var fs = require('fs');
    var urlList = require('./criticalcss-pagelist.json')
  3. Add a variant of the following to the gulpfile.js. We’re writing the output as PHP files wrapping around the CSS, as we’re going to be inserting the Critical CSS for each page into the <head> section via PHP but you can adjust the fs.writeFileSync() to your needs. The below includes all options but you of course don’t need to include them all
    // Run:
    // gulp criticalcss
    // Generates CriticalCSS for above-the-fold content - look to criticalcss-pagelist.json for list of input pages and output files
    gulp.task('criticalcss',function(){
      urlList.urls.forEach(function(item,i){
        penthouse({
          url: item.link,  // can also use file:/// protocol for local files
          css: './css/style.min.css',  // path to original css file on disk
          width: 1300,  // viewport width
          height: 900,  // viewport height
          keepLargerMediaQueries: false,  // when true, will not filter out larger media queries
          forceInclude: [ // selectors to keep, useful for above-the-fold styles added by js scripts
            '.keepMeEvenIfNotSeenInDom',
            /^\.regexWorksToo/
          ],
          propertiesToRemove: [
            '(.*)transition(.*)',
            'cursor',
            'pointer-events',
            '(-webkit-)?tap-highlight-color',
            '(.*)user-select'
          ],
          userAgent: 'Penthouse Critical Path CSS Generator', // specify which user agent string when loading the page
          puppeteer: {
            getBrowser: undefined, // A function that resolves with a puppeteer browser to use instead of launching a new browser session
          }
        })
        .then(criticalCss => {
          // use the critical css
          fs.writeFileSync('./'+item.name+'.php'," ");
        })
        .catch(err => {
            // handle the error
        })
      })
    })
  4. Add a list of URLs to parse and a list of files to write by adding a variant on this to a criticalcss-pagelist.json file. The "link" is the page to parse for Critical CSS based on the configuration in your gulpfile.js and the "name" is the name of the file containing the above-the-fold Critical CSS to output for each corresponding page. In this case we’re referencing local pages but you can use live pages (though it’s slower).
    {
      "urls": [
        {
          "link": "https://local-alexwright.net/",
          "name": "criticalcss/home"
        },
        {
          "link": "https://local-alexwright.net/westlake-plastic-surgery/",
          "name": "criticalcss/portfolio-project"
        },
        {
          "link": "https://local-alexwright.net/free-seo-analysis/",
          "name": "criticalcss/seo-analysis"
        },
        {
          "link": "https://local-alexwright.net/free-seo-analysis-report/",
          "name": "criticalcss/seo-analysis-results"
        }
      ]
    }

    It’s worth noting that if you run a lot of pages all at once, Penthouse can time out. Check this great reference for information on batching up Penthouse runs so your poor computer doesn’t explode.

  5. Run gulp criticalcss and watch as computers do what they’re good at – lots of busywork
  6. Add each generated Critical CSS element to the <head> of each page as needed. We’re in a WordPress workflow, so we add something like this into the header.php file
    <?php if( is_front_page() ) :
    get_template_part( 'criticalcss/home' ); 
      elseif( in_category( 13 ) ) :
    get_template_part( 'criticalcss/portfolio-project' );
      elseif(is_page ( 382 ) ) :
    get_template_part( 'criticalcss/seo-analysis' );
      elseif(is_page ( 384 ) ) :
    get_template_part( 'criticalcss/seo-analysis-results' );
      else :
    endif ?>

    Another option to accomplish this in a WordPress workflow is to use the wp_head() action hook in the functions.php

    add_action( 'wp_head', 'criticalcss');
    function criticalcss() { 
     if( is_front_page() ) {
        get_template_part( 'criticalcss/home' );
     }
     else if( in_category( 13 ) ) {
        get_template_part( 'criticalcss/portfolio-project' );
     }...
    }

    While this works just fine, the first solution of adding conditional tags directly in the head works to get the Critical CSS files higher up in the head section, which is better as the browser gets to the Critical CSS files sooner.

  7. We then use Autoptimize to – among other things – defer our single main style.css file into the footer. Another alternative, or if you want to do this manually, you can use Filament Group’s loadCSS (which Autoptimze uses). Even large, complex websites can get 95 and above on Google PageSpeed Insights (try running this website through that tool)

Are you using a high caliber Critical CSS workflow? Or does none of this make any sense at all? Are you drinking beer, or coffee? Let us know in the comments. And mega special thanks to Jonas Ohlsson Aden for all his work on Penthouse and for suggesting great changes to this post including making things actually correct.

Update

Updated to include new Penthouse functionality – thanks to Jonas Ohlsson Aden. He’s working on a new way to batch up larger runs of Critical CSS generation and we’ll update this post with those details when we get them.

blog post author avatar
Written with by Alex Wright

Runs a creative digital marketing, web design and SEO agency in Austin, Texas   |   Likes good looking, fast performing and high ranking websites   |   Doesn't want to understand Snapchat   |   Wants nothing to do with bananas   |   Wishes he were a BMX superstar

28 thoughts on “How To Use Critical CSS

  1. Hello, just to be clear, the critical css generated from the main css file is not extracted from it. The critical css is now in now duplicated, and is now both inline And still in the main css file?
    We can’t ask the script to generate the critical css from multiple html files and have the generated critical css removed from the main css?

    1. Chuck, you’re correct. The term “extracted” does imply that the critical css is pulled from the main css file then removed from it. You’re correct that the criticalcss gets duplicated. While this is definitely something to think about finding a solution for, the duplicated criticalcss usually weighs about 8kb – 12kb, and that’s not giant. Another issue to think about is that page A may have X css property in the criticalcss while page B has Y property in the criticalcss. If page A also uses Y css property further down the page (not in the above-the-fold area), we can’t remove Y css property from the main css file, as it’s needed by page A. So any system that will truly extract (and remove) the criticalcss from the main css file will have to take into account what’s not critical below the fold content on all other pages. That’s a lot of calculations!

      1. OK I understand now, I’m going to give it a try asap, you’re right having around 10 kb of duplicated css is not a big deal, thank you so much for the blazing fast answer!

        1. So, couldn’t sleep, the nerd in me was too excited to try your technique so I just did some tests with Page Insight and I won 0.3 seconds using critical css ! I went from a 1.4s First Contentful Paint to a 1.1s First Contentful Paint, same for the First Meaningful Paint. F.I.D is unchanged. Score : 100 for both mobile & desktop (99 prior to changes).
          What I discovered too is defering with JS the main CSS is slower than just putting the CSS at the end on the file as you advise. Is it because my web server (nginx) is too fast to benefits from a defer ? Thank you again Alex.

  2. Interesting read. Thanks!

    Personally, I’ve noticed that as long as your HTML and CSS is no more than 50 – 60 kb, it isn’t worth the hassle of implementing critical CSS, but I guess every little drop of performance adds up.

    1. That’s a good point. Many people’s collected CSS files can be in the hundreds of kb even approaching 500kb if using a framework. If you have a clean custom setup with 60kb total in markup and stysheets, you’re off to the races and probably don’t need criticalcss at all.

  3. Thanks for this.. My Critical CSS engine now works.
    However in regards to the (after render loaded) non-critical CSS, my PSI says I still need to defer unused CSS. Meaning that the page still loads a lot of styles that the page doesn’t need… So my question is: Can’t we use the Puppeteer and Penthouse to just set the Critical Height parameter to, let’s say, 10000px, and the let penthouse pick out the exact styles needed for each page?

    Tried but it seems it doesn’t work for me … :O

    1. What we use in addition to Critical CSS is UnCSS – using a PostCSS plugin to go through the entire compiled css file, compare it against a list of pages and then generate a file of only the css used in the pages. This is particularly great for big bloated frontend frameworks like Bootstrap that contain many styles that are not always used. We often see reductions from an initial compiled stylesheet of ~700kb down to an amazing final ~80kb.

      Use this technique in addition to Critical CSS to really reduce your page load times. Maybe another blog post is in order…

      Note that even if you use both approaches, you’ll likely still see Google Lighthouse warnings to defer unused CSS. This is because Lighthouse looks at only one page and compares the needed styles for that page against the stylesheet loaded for that page. Most likely you have one stylesheet for your whole site, and surely there are styles there that each page may not need. So Lighthouse will pick up on that and throw a warning. But the only alternative to this is to load a single stylesheet per page, and that’s a bit silly as the user would then need to re-load a stylesheet specific to every page.

      Lighthouse can audit one page at a time. For a whole website, using Critical CSS and UnCSS together is a great approach.

      1. Thanks Alex! Will look in to UnCSS! 🙂

        But you then confirm, that my Critical CSS setup works? (punched in my webaddress), or at least setup correctly?
        I mean, the PSI “Defer unused CSS”, still registers my entire compiled css file (styles.min.css) as a prime bad souce in this section. And can you confirm that this is due to what I write above, with unused CSS? Or is it somehow my rel=”preload” setup that is setup wrong?

  4. Hi Alex, I am getting an error;

    [15:42:13] The following tasks did not complete: criticalcss
    [15:42:13] Did you forget to signal async completion?

    If I manually finish the task it doesn’t write the files.

      1. Hi Alex, thanks for getting back to me. I managed to fix it;

        gulp.task(‘critical’, done => {

        urlList.urls.forEach(function(item,i){
        penthouse({
        url: item.link, // can also use file:/// protocol for local files
        css: ‘./assets/css/styles.min.css’, // path to original css file on disk
        width: 1300, // viewport width
        height: 900, // viewport height
        keepLargerMediaQueries: true, // when true, will not filter out larger media queries
        forceInclude: [ // selectors to keep, useful for above-the-fold styles added by js scripts
        ‘.keepMeEvenIfNotSeenInDom’,
        ],
        propertiesToRemove: [
        ‘(.*)transition(.*)’,
        ‘cursor’,
        ‘pointer-events’,
        ‘(-webkit-)?tap-highlight-color’,
        ‘(.*)user-select’
        ],
        userAgent: ‘Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)’, // specify which user agent string when loading the page
        puppeteer: {
        getBrowser: undefined, // A function that resolves with a puppeteer browser to use instead of launching a new browser session
        }
        })
        .then(criticalCss => {
        fs.writeFileSync(
        ‘./assets/css/’+item.name+’.php’,
        criticalCss);
        })
        .catch(err => {
        // handle the error
        })
        });

        done();

        });

    1. This is a really good question. The best answer is … don’t use any plugins that insert css into the <head>. When we build custom websites, we don’t use any front-end plugins and all styles are compiled together in a single stylesheet using Sass. So it’s easy then to extract the Critical CSS and move the single stylesheet to the footer using Autoptimize or another method.

      But of course that’s not always possible. If you have a website that requires a plugin that inserts css into the <head>, I’d recommend un-enqueuing the plugin’s stylesheet in your functions.php and instead including the css in your Sass workflow so that the plugin’s styles are incorporated into a single stylesheet.

      Here’s how we unenqueue plugin styles:

      /* REMOVE PLUGIN STYLESHEETS
      ================================================== */
      // Remove various plugin stylesheets, include in main styleshee
      
      add_action( 'wp_print_styles', 'custom_remove_plugin_styles', 30 );
      
      function custom_remove_plugin_styles() {
      
        wp_deregister_style( 'fontawesome' );
        wp_deregister_style( 'ngg_trigger_buttons' );
        wp_deregister_style( 'nextgen_basic_thumbnails_style' );
        wp_deregister_style( 'nextgen_pagination_style' );
        wp_deregister_style( 'gforms_reset_css' );
        wp_deregister_style( 'gforms_formsmain_css' );
        wp_deregister_style( 'gforms_ready_class_css' );
        wp_deregister_style( 'gforms_browsers_css' );
      }

      And then we include them in a Sass or other css compiling workflow like this:

      @import "../../plugins/gravityforms/css/formsmain.min.css";
      @import "../../plugins/gravityforms/css/readyclass.min.css";
      @import "../../plugins/gravityforms/css/browsers.min.css";
      @import "../../plugins/gravityforms/css/formreset.min.css";
      @import "../../plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_basic_gallery/static/thumbnails/nextgen_basic_thumbnails.min.css";
      @import "../../plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_pagination/static/style.min.css";
      @import "../../plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_gallery_display/static/trigger_buttons.min.css";
      @import "js/fancybox/jquery.fancybox.css";

      You can find the id of the stylesheet to use in the wp_deregister_style() function as well as the path to the stylesheet by inspecting the source of the page with the plugin. Just make sure to take the last trailing -css off of the stylesheet id – WordPress includes that automatically but you have to remove that in order to unenqueue that stylesheet using the wp_deregister_style() function.

      Let me know if that helps / makes sense / doesn’t make sense.

  5. Thanks so much for sharing Alex, great tips!

    Quick question: how does simply loading css file at the bottom of the page compare to using loadCss? In my limited tests on localhost I don’t see any measurable difference in DOMContentLoaded. I’m using Filament’s example ‘Manual CSS loading with loadCSS’ (i.e. without the preload attribute).

    1. Thanks for the kind words, Peter : )

      Putting an externally linked stylesheet in the <body> isn’t valid HTML. That’s the only reason I know of to use loadCss. It puts the stylesheet technically in the <head> (which is “proper” valid HTML), uses <link rel="preload" href="path/to/mystylesheet.css" as="style"> for browsers that support link rel="preload", moves the stylesheet to the bottom of the <body> with JavaScript for browsers that don’t support <link rel="preload" href="path/to/mystylesheet.css" as="style"> and for browsers that don’t use JavaScript, it includes a regular old school external stylesheet link in the <head>. But the end result is the same. I don’t think there are any performance benefits.

      I’ve used externally linked stylesheet in the bottom of the <body> of websites and all browsers deal with it fine. It’s just not technically valid.

      Maybe someone else knows a different / better reason for using loadCss?

      1. Alex, thank you for taking the time to clarify the benefits of using loadCss vs. including non-critical css file at the bottom of the page. For me, this was THE missing link in my endeavors to speed up my website!

  6. Hi there this is somewhat of off topic but I was wanting to know if blogs like this use WYSIWYG editors or if you have to manually code with HTML. I’m starting a blog soon but have no coding knowledge so I wanted to get advice from someone with experience. Any help would be enormously appreciated!

    1. It’s tough to say these days if that comment is real or not. If it’s not real, the bots sure have gotten better! Truth is, you can’t really use a WYSIWYG editor do write technical blog posts because there’s so much insertion of code snippets and other content. And no WYSIWYG editor makes any of that easier. Gutenberg included.

    1. more time than you might think, and on a bigger site like it can really add up. conversion rates go up a lot when loading time goes down even my small amounts.

Leave a Reply

Your email address will not be published. Required fields are marked *