thoughtexpo

... @leelavg's corner on the internet, night or day, small or big.

Setup Hugo With Tailwindcss for Netlify

2020-Aug-23 • Tags: hugo, tailwindcss, netlify

When I have come across Tailwind CSS for creating a UI for my personal Hugo powered blog I found little information on integrating both with Netlify. I wish to cover three methods (least to most productive) for setting up a hugo blog with tailwindcss and deploying to Netlify in this blog post.

As my experience with blogging and web technologies in general is very less, apart from documentation I referred below resources to get upto speed, a huge thanks for those content creators and I highly recommend them as well.👏

All the methods are tested against a single html page from TailwindCSS playground and please follow along for some hands on.

I assume that you have a little knowledge about TailwindCSS, NodeJS and ecosystem around it, hugo blog structure (have a look at this post after hugo's quick start).

Method 1: Pulling TailwindCSS from CDN §

Method 1 is the easiest out of all three as this involves only pulling a CSS file from a CDN and linking it in your html pages (typically in baseof.html of hugo layouts).

As we'll be using a single html page we can refactor it to match recommended hugo project layout as a follow up.

Below repo holds required files to demonstrate all three methods but we'll be cloning one branch at a time corresponding to our method:

-> git clone --single-branch --branch method1 https://github.com/leelavg/examples.git ~/method1 && cd "$_"

We are interested in only head section of index.html file from this repo where we are linking TailwindCSS from CDN.

-> sed -n '4,11p' layouts/index.html

4<head>
5 <meta charset="utf-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
7 <link rel="icon" type="image/png" sizes="32x32" href="images/favicon-32x32.png">
8 <link rel="icon" type="image/png" sizes="16x16" href="images/favicon-16x16.png">
9 <link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
10 <title>Welcome to Tailwind!</title>
11</head>

Method 2: Using PostCSS (Without Hugo Pipes) §

TailwindCSS is a PostCSS plugin which potentially transforms CSS with Javascript, I would describe it in a crude sense as a tool which bisects words into alphabets and hands over to various plugins to act upon resulting in most cases, a CSS file which can be directly consumed by html pages.

There're new entries involved in method 2 which effectively utilizes NodeJS directly.

-> git clone --single-branch --branch method1 https://github.com/leelavg/examples.git ~/method2 && cd "$_"

On surface below will be the change in index.html when compared to other methods:

-> sed -n '4,11p' layouts/index.html

4<head>
5 <meta charset="utf-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
7 <link rel="icon" type="image/png" sizes="32x32" href="images/favicon-32x32.png">
8 <link rel="icon" type="image/png" sizes="16x16" href="images/favicon-16x16.png">
9 <link href="{{ "css/tailwind.css" | relURL }}" rel="stylesheet" />
10 <title>Welcome to Tailwind!</title>
11</head>

Below is the flow how this site is built in a production context when deployed to Netlify:

Method 3: Using PostCSS (With Hugo Pipes) §

We'll be using Hugo Pipes feature which implicitly calls postcss-cli on demand during hugo build process eliminating the need for separate build of CSS file and I recommend referring @regisphilibert wonderful post on hugo pipes, in fact his whole blog is a good read for learning about hugo.

Lookout for changes in config.yaml, tailwind.config.js and this method greatly slims our package.json.

-> git clone --single-branch --branch method1 https://github.com/leelavg/examples.git ~/method2 && cd "$_"

When NODE_ENV is set to production hugo will minify, fingerprint post-processed CSS (refer below listing) and tailwind (>= v1.4.0) will purge unused styles.

-> sed -n '4,15p' layouts/index.html

4<head>
5 <meta charset="utf-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
7 <link rel="icon" type="image/png" sizes="32x32" href="images/favicon-32x32.png">
8 <link rel="icon" type="image/png" sizes="16x16" href="images/favicon-16x16.png">
9 {{$css := resources.Get "css/tailwind.css" | resources.PostCSS}}
10 {{if (eq (getenv "NODE_ENV") "production")}}
11 {{$css = $css | minify | fingerprint | resources.PostProcess}}
12 {{end}}
13 <link rel="stylesheet" type="text/css" href="{{$css.Permalink}}">
14 <title>Welcome to Tailwind!</title>
15</head>

-> cat tailwind.config.js

1module.exports = {
2 theme: {
3 extend: {},
4 },
5 variants: {},
6 plugins: [],
7 purge: {
8 content: ["./hugo_stats.json"],
9 defaultExtractor: (content) => {
10 let els = JSON.parse(content).htmlElements;
11 return els.tags.concat(els.classes, els.ids);
12 },
13 },
14}

In method 2, purge CSS stores the tags using a custom regex but here we'll be delegating that task to hugo by setting writeStats to true under build section of config.yaml introduced in v0.69.0.

The site will be built in almost similar manner as described in method 2 but hugo deals with running postcss utilizing necessary tools and builds final CSS file which then be placed in public before serving website.

Miscellaneous: §

Hugo combined with Tailwind CSS provides many other features and this post intentionally limits itself at briefly showing the ways to integrate these two and deploying to Netlify. I believe below points will be helpful when you try to build a more feature rich blog on your own:

Expressing only thoughts without practice in a sense is futile, so here are the links for final results Method1, Method2 and Method3, these are being served from Netlify as all the repo branches stated above contains a netlify.toml and observe stylesheet link in view-source(ctrl-u) of above html pages.

Send an email for any comments. Kudos for making it to the end. Thanks!