Embed CSS in HTML head for Critical Render Path (CRP)

For my static assets (js, css, images), I use a gulp build to minify my css and process all my sass files. I have it writing to a partial titles embed-style.html. When I try to include this partial in the <head> of my layout between two <style> tags, it writes about a dozen random characters rather than embedding the minified CSS into the HTML (my goal since this is better for CRP). Any thoughts on how to escape this so that just the exact text writes to the page? I tried the pipe safeHTML , but no luck.

I tried adding <style></style> to the embed-style.html file in my partials, but that didn’t make a difference. Regardless, here is the output I’m getting:

<style> ZgotmplZ </style>
In the <head> of my HTML.

Use safeCSS.

@moorereason Unfortunately, this isn’t working for me. Basically, my gulp workflow writes all the minified CSS to partial/style-embed.html. I then try to call that partial in with the following and am still having zero luck:

<head>
    <meta charset="utf-8">   
    <meta http-equiv="X-UA-Compatible" content="IE=edge">  
    <meta name="viewport" content="width=device-width, initial-scale=1">  
	<title>{{ if .Title }}{{ .Title }} | {{ end }}{{ .Site.Title }}</title>  
    <style>  
    {{ partial "style-embed.html|safeCSS". }}  
    </style>  
	{{.Hugo.Generator}}  
</head>

Or trying with {{partial "style-embed.html"|safeCSS .}} also doesn’t render. I get the following error in the console:

ERROR: 2015/12/07 template: partials/global_header.html:9:34: executing "partials/global_header.html" at <safeCSS>: wrong number of args for safeCSS: want 1 got 1 in partials/global_header.html

Trying safeHTML also doesn’t work. Also tried writing the css to style-embed.css and with both safeCSS and safeHTML as well. No dice. Hmmm.

Try

{{partial "style-embed.html" . | safeCSS }}

@bep Thanks for throwing in a suggestion as well. I appreciate it, but regrettably this doesn’t work either when including the following in my head.html partial:

<style>
	{{partial "style-embed.html" . | safeCSS }}
  </style>

Why don’t you have gulp output to a CSS file in static and then just reference it from your html head?

@moorereason Haha, fair enough. That is pretty standard, but this is stemming from an obsession I have with performance. Check out here and this article on CRP. At the end of the day, it’s not a huge deal if I set up caching correctly, but I’m still exploring Hugo and want to make sure it can do everything I currently can do with Jekyll. (It basically can, and it’s exponentially faster.) Thanks for all your help!

I just wanted to make sure you had a good reason for doing what you were doing. I’m still interested in getting this CSS embedding to work for you.

I can confirm that {{ partial "css.html" . | safeCSS }} doesn’t work. I get an error:

 wrong type for value; expected string; got template.HTML

So, partial returns a rendered HTML template, not a string, and it’s too late to use safeCSS at that point.

Go templates are context-aware. See the safe* function descriptions in the Template Functions section of the Hugo docs. That’s where the ZgotmplZ string is coming from.

So, the CSS context needs to be in the partial template. To do that you need to move your <style> tags into your embed-style.html file.

A hackish way would be:

{{ print (partial "css.html" .) | safeCSS }}

The above should give you a string suitable for safeCSS.

Thanks @moorereason and @bep. I’m going to try and work with gulp-inject later this afternoon to see if I can get it working. The nice thing is that I can then remove the livereload from my gulp build and rely on hugo --server to refresh the page instead. More to come shortly. Cheers.

So after fiddling with a variety of different gulp plugins, your workaround actually works perfectly @bep. Thanks so much!

@bep @moorereason In a related question, is there a built-in variable for dev vs prod? If yes, is there a default setting so that I don’t need to add another flag in the command line? Again, this is just fiddling with the SSG and comparing it with some other static generators I’ve worked with. So, something like the following:

{{ if .Site.env == "dev" }}
<link rel="stylesheet" type="text/css" href="/css/style.min.css"> 
{{ else }}
<style>
{{ print (partial "style-embed.html" .) | safeCSS }}
</style>
{{ end }}

This is a bit of a tenuous example (Google analytics would have been better). Thanks again to you both. I really appreciate it.

1 Like

No, but this works for me:

{{ if not .Site.BuildDrafts }}
<script>
    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
            m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
    })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

    InstantClick.on('change', function() {
        ga('create', 'MY-ID', 'auto');
        ga('send', 'pageview');
    });
</script>
{{ end }}