Hugo Inline CSS Optimization for Google PageSpeed

I’ve been trying to find a nice way to do this, and this is what I’ve just come up with…

<style>{{ readFile "static/critical-86260bc8.css" | safeCSS }}</style>

Works pretty well. I have it hooked up to data files which I use as an asset manifest file. This method still works even with all of that funky stuff in place :slight_smile:

5 Likes

OHhhhhh YOUUURRRR the AfterDark guy… awesome.

This looks great! Yoink I’m stealing that pattern TYVM. :smiley:

How do you manage several pages?
For example, I have inlined my critical css for my homepage, but I also want to do it for all my blog posts. So, in theory, I should have a different critical css file for my blog post template.

You could use a tool like critical to render the critical CSS for each page.

I think it’s a bit worse (in terms of work required). You likely need different critical CSS files for your blog posts, because some might have an image above the fold, or a blockquote, or a code example, etc. So not every (type of) post has the same critical CSS.

If you have access to HTTP/2, it’s a better idea to use Server Push for the CSS file instead of bothering with determining critical CSS. That gives you multiple advantages over inlining CSS; see this blog post for more information: HTTP/2 for web developers.

2 Likes

Thanks, I’ll read more about that

1 Like

With Hugo 0.46 you should be able to use resources for that.

Example of SCSS

{{ with resources.Get "scss/styles.scss" | toCSS | minify }}
<style>{{ .Content | safeCSS }}</style>
{{ end }}

or CSS

{{ with resources.Get "css/styles.css" | minify }}
<style>{{ .Content | safeCSS }}</style>
{{ end }}

Note that resource files needs to be located under /assets directory (not /static)

13 Likes

How would you find exactly which CSS is needed for above the fold content if you’re using bootstrap or another css framework?

Being a designer I like to make my own layout and typographic choices.

So I write my own CSS.

I marked your reply as the solution since this is the way to do it now.

(Also I didn’t have the time to update this old Tips and Tricks)

There are JavaScript libraries for that but if you’re like @alexandros and like to make your own design choices check out the Sass file structure in Michael Roses’ Minimal Mistakes theme for how he did it.

EDIT: Actually don’t look at his theme I forgot it doesn’t contain critical path CSS optimizations. But this does and I explain it in more detail here.

You can use Hugo + Hugo pipes to generate critical and non-critical stylesheets.

I wrote a tutorial on how to do it here.

That’s a hefty post. To summarize we do this right? As mentioned in the selected answer.

<style>{{ resources.Get "css/theme.css" | minify | safeCSS }}</style>

EDIT: Ah, sorry missed it the first time. You’re adding to the second question of how to decide which CSS to load as critical. Given you’re concatenating CSS by layout block wouldn’t that necessitate duplication between modules?

@alexandros could we break this into a new discussion?

Sorry the post is definitely huge. I tried to remove as much as possible, but it’s hard to condense it down even further.

If your theme uses basically the same layout for each page then you may as well manually generate the critical CSS using a tool like this one and inline it with resources.Get.

If you use a lot of varying layouts then you might benefit from conditionally generating the critical and non-critical CSS based on the sections in use.

The Generating our critical inline style section goes into more detail.

I’m sure the method isn’t perfect though I did show a basic version of my current setup because it’s too involved for the tutorial. I’d definitely like to discuss more ideas here.

1 Like

That’s definitely the relevant section. Strange Medium seems to be stripping deep links. The section provides an approach for how blocks, defined as modules, are used to inline priority CSS variadically into the document head when building for production.

Is it ever necessary to have to update CSS in more than one place when modifying styles under the critical path selection approach? If so, do you think it possible to use replaceRE to remove any duplicative output from the generated styles on build?

Aside, I think I saw it in a separate section you may be using link tag outside the header to load non-priority CSS. Did you find a way to prevent blocking the CSSOM when that link is hit, such as async loading via Ajax or Fetch, to prevent FOUC and unblock the page load.

Separate files have always been a big mistake. It’s related to a similar problem – massive amounts of CSS that go unused. The caching argument rarely survives empirical testing, which is why AMP puts all the CSS in the head.

1 Like

To my knowledge, there’s not a good way to exclude certain critical base styles from the non-critical stylesheet. This is really a non issue for most simple layouts. However, this is a problem if you use a slider or another larger component in the header. Do we really want to inline the slider styles in the header while also adding it to the non-critical stylesheet? Probably not a good idea.

I actually use your fetchinject plugin for scenarios like this. When you’re using CSS/JS with dependencies (or the base style is much larger), it’s better to use fetch inject on that page to lazyload those associated styles.

1 Like

HTTP/2 helps with this a lot. You can have separate files that are all cached separately - thus invalidating less code when a minor change happens - and they are all downloading on the same connection to the server, instead of one connection for each resource. HTTP PUSH also helps by having the same effect as inlining, while still keeping the benefits of caching a separate file. Then you can separate styles into related files and not include unnecessary styles on a page.

This Tips & Tricks has went very OT.

Thank you for your contributions everyone.

2 Likes