A battle with a tank coming out of a forest

The Critical Path

So over the past six months I have made a concerted effort to up my game when it comes to front end performance. I have been using Grunt for a minute now with some of the easy yet tedious stuff. The low hanging fruit has been concatenation and minification. Give Grunt some source and destination paths and you just gave your self faster load times. While these have been a big time saver for me and helped cut my HTTP requests and page size down I knew that if I wanted to get my pages super fast I needed to integrate this critical path rendering I have heard so much about.

Finding The Path

So the first thing I needed to do was to determine exactly what is deemed critical. I use Grunt and if you need help getting started there is a nice guide here. So with Grunt you have two options. They are the same for the most part with only one major difference from what I can see.

For my purposes I went with grunt-criticalcss. I like the idea of having my task just create a file. This seemed easier to integrate with the rest of my workflow and less messy when dealing with WordPress. I have a fairly simple Grunt setup and it can broken down into to two tasks.

  • default task
    1. compiling Sass
    2. watch for changes and trigger livereload
  • production prep task
    1. CSS minification
    2. JS concatenation
    3. uglify (JS minification)

It makes the most sense to add my critical task right before minification happens in my production task. This way the file will get prepped before it is in-lined into my header file. Here is my configuration for the task.


    criticalcss: {
          custom: {
            options: {
                url: "http://your.vhost.com",
                width: 1024,
                height: 800,
                outputfile: "../critical.css",
                filename: "../style.css",
            }
          }
        },
  

Deciding on a width and height for a view at which to grab what is critical kind of threw me for a loop. Determining what’s important based on view port doesn’t seem like a good idea. I had to choose though so for the the width and height in this particular example I chose the width based on a media query. 1024 is a break point that represents a major shift in layout. This seemed like a nice happy medium with the overall view port landscape and because of how my CSS is structured a good size to minimize jankiness when loading on larger view ports.

Traversing the Path

Now that I have my critical.css file I need to figure out how integrate it with my WordPress and Grunt workflow. I needed to make sure of a couple of things with the implementation.

  1. That nothing interferes with my development process. I can’t be dealing with editing CSS in a functions file (Messy) or the head of my document (Confusing).
  2. Load the rest of my non-critical CSS asynchronously. This makes the remaining CSS not block the rendering of content.

Here is a WordPress function that accomplishes those goals and fits into my Grunt workflow. Read the disclaimer


  // function for critical path css
  function critical_css() {
    if ( WP_DEBUG || SCRIPT_DEBUG ) {
      wp_enqueue_style( 'style', get_stylesheet_uri('style.css') );
    }
    else {
    // css
    $crit_sheet = get_template_directory_uri() . '/critical.css';
    $critical_path = file_get_contents($crit_sheet);

    // loadCSS
    $js_sheet = get_template_directory_uri() . '/js/loadCSS.js';
    $loadCSS_path = file_get_contents($js_sheet);

    echo '<style>'. $critical_path .'</style>' . "\n";
    echo '<script>'. $loadCSS_path .'</script>' . "\n";
    echo '<script>loadCSS("'. get_stylesheet_uri(). '")</script>' . "\n";
    echo '<noscript><link rel="stylesheet" href="'. get_stylesheet_uri(). '"></noscript>' . "\n";
    }
  }
  add_action( 'wp_head', 'critical_css');
  

So I first look to see if we are in production or development using debug as indicator. If debug is set to true (development) I wp_enqueue_style my style sheet as WordPress normally does. If debug is set to false (production) I write in my prepped assets to the head of the document. lets take a look at what we are loading:

  1. $critical_path adds the critical CSS stuff directly into the head and is loaded first.
  2. $loadCSS_path adds in LoadCSS. This is a great plugin made by the lovely people over at the Filament Group. It uses JavaScript to load a specified stylsheet asynchronously.
  3. loadCSS("'. get_stylesheet_uri(). '") loads in the full stylesheet for use with LoadCSS.
  4. and finally our stylesheet grabbed again and wrapped in a <noscript> tag for when JavaScript fails.

So this has all fit nicely into my WordPress workflow with only a couple of issues I have yet to sort out.

Caution ⚠️

So the first thing I am unsure about is if there are any security concerns with the way I am using get_file_contents. My research did not yield a definitive answer and I am by no means an expert with security. There is also an issue at times with running the actual criticalCSS task. It appears as if PhantomJS doesn’t like external scripts. When I run the task with these scripts loading phantom will not finish. When I remove external scripts the task finishes without issue.

If you have any suggestions on improving this I would love to hear from you. You can also yell at me on Twitter if that works better for you.

Back to code sample

Image attribution—By SuperTank17 (Own work) [GFDL or CC BY 3.0], via Wikimedia Commons


Also published on Medium.