tldr; Here is a working demo: https://github.com/Gioni06/hugo-pipes-tailwind-3
TailwindCSS is a utility-first CSS framework for rapid UI development and as of version 3, the JIT mode is the new default. Among other benefits, it renders a CSS file, that only contains the code, that your site is actually using -- nothing more!
1. What changed since TailwindCSS 3
Since version 3, TailwindCSS only uses the JIT compiler to do its magic. That means, that utility classes will be generated on demand instead of purging unused classes afterward. The thing with TailwindCSS is, that only the compilation result changes, not the input file. For this reason, Hugo Pipes won't work beyond the initial render pass. This behavior is perfectly fine for a production build, but it doesn't detect changes when running a Hugo development server in "watch" mode.
{{/* Doesn't work in "watch" mode */}}
{{ $style := resources.Get "tailwind.css" | resources.PostCSS | resources.Minify }}
2. How to use TailwindCSS without external NPM scripts, just Hugo Pipes
First up, we use TailwindCSS as a PostCSS plugin. This is key to using Hugo's build-in resources.PostCSS
pipe. You can read how to setup PostCSS with TailwindCSS here: https://tailwindcss.com/docs/using-with-preprocessors#using-post-css-as-your-preprocessor
To make work in both production and development the "Hugo Way", I came up with a little hack. Rather than using the input file as a regular CSS resource, we can tell Hugo to treat it like a template file using the resources.ExecuteAsTemplate
pipe.
To ensure that the result always changes, we can create a random string and pass it into the CSS input template. Also, the final CSS file has the generated random string as part of its file name.
Note that this is only necessary during development. In production, a single render pass by Hugo is all it takes to work with TailwindCSS.
This is the complete code that I came up with.
{{ if .Site.IsServer }}
{{ $seed := "weqklrjfmnk213409ufasdfhnlk3j4bladsfsl" }}
{{ $random := delimit (shuffle (split (md5 $seed) "" )) "" }}
{{
$style := resources.Get "tailwind.css"
| resources.PostCSS
| resources.ExecuteAsTemplate (printf "tailwind.dev.%s.css" $random) $random
}}
<link rel="stylesheet" href="{{ $style.RelPermalink }}">
{{ else }}
{{
$style := resources.Get "tailwind.css"
| resources.PostCSS
| resources.Minify
}}
<link rel="stylesheet" href="{{ $style.RelPermalink }}">
{{ end }}
3. Conclusions
In this article, we discussed how to use TailwindCSS without external NPM scripts by using Hugo pipes. First, we discussed how to use the JIT mode in TailwindCSS 3.0. Next, I showed how to use Hugo pipes to treat the input file like a template so that it always triggers a re-compilation when running Hugo in "watch" mode.
Top comments (2)
Excellent article, thanks!
There is a downside, though - the resources folder is polluted now with the useless junk and cannot be checked in to Git anymore. It is in Git for me because image resizing is a CPU-costly operation, and I prefer to store the computed results in Git to avoid recalculation.
Thanks ;) You are totally right, that is a real downside if you rely on this folder to be under version control