Using css.TailwindCSS vs npx tailwindcss/cli

Hello folks,

I’m wondering what the advantages are of running tailwind via Hugo versus just using the tailwind cli to create a css file Hugo then consumes.

I’d welcome your thoughts to help me decide on which method to use :slight_smile: (I’m planning on using tailwind v4, BTW)

Many thanks!

1 Like

What’s the advantages of doing it the other way around?

Some advantages from top of my head:

  • One build/server command
  • css.TailwindCSS is a first class citizen in the Hugo build chain, e.g. support for fingerprinting/SRI/minification, passing of Hugo environment to Tailwind, inline imports (with module resolution, import from /assets etc.)
  • Change/rebuild detection, hugo_stats.json …

I could write a longer story here, but I would never even consider using Tailwind CLI directly.

1 Like

Thanks @bep. I probably chose the wrong word in ‘advantage’ — in that its use could be subjective.

One angle where I could see it being simpler is running tailwind to generate the CSS file outside of Hugo makes the Hugo template simpler. Barely, I know, but to me it would be simpler. It also makes it transparent that tailwind is in play, versus making other people who might be maintaining the same setup go digging through Hugo template files.

Forgive the question if it’s obvious, but what advantage does hugo_stats.json bring? Surely a tailwind cli build would be parsing the HTML files to generate a pruned and efficient CSS file anyway?

My choice of the word ‘advantage’ may have been poor. It was alternative perspectives I was looking for, that I might not have thought of. I was, am, automatically gravitating towards using the integration. I wanted to find out how I might be wrong :smiley:

Your input has been welcome, thank you :folded_hands:

First, your arguments about making the “templates simpler” is very … vague. It’s a one liner vs a “few liner” that you write once.

As to hugo_stats.json – that can be used to record any id/class/html element used, which is the foundation for Tailwind to do its job. You can argue that TW is doing this without this info, but that’s only true for simpler setupsø

But making this less one-sided; can you mention some benefits of having a separate build step for this?

apart from possibly making the template simpler (which it’s not once you start needing fingerprinting etcl)

Oh, don’t mistake my question as an argument for one method over the other @bep — like I said, my gut instinct is to use the built in process you’ve so nicely engineered :smiley:

I like to challenge my gut feelings by looking for how I might be wrong though, so I’ve come looking for people’s thoughts. Which you’ve gratefully shared, thank you.

Playing devil’s advocate then, on the side of ‘keeping it simpler’, to an extent — personally speaking — I find the thought of running the tw cli command, whilst developing a layout, and then just consuming the resulting CSS in Hugo, simpler.

One has to install tailwind anyway, but then to use it in Hugo there is config to enable the cachebuster — the documentation for which I find a little confusing (is assets/watching a path I create? Does enabling hugo-stats create it? The doc page mentions tw 3, is it still relevant for tw 4?)

BTW, I’m more than happy to contribute to the docs if raising these questions means changes are needed. I’ve spent 30 years in tech, the early part with the largest ISP in the world, and the last 10 years in product management :smiley:

Yes, that is a point. If your process is 1) Spend 50 hours developing a theme and 2) Spend the next 3 years adding content and not doing anything to that layout/CSS, then that may be a valid approach. I would probably then write the CSS from Tailwind into /assets so you can still have minification/fingerprinting etc. if you want.

But … the nature of TailwindCSS is that changes in TailwindCSS classes used in a Hugo project isn’t normally very stable, e.g. shortcodes or class attributes from Markdown, so I suspect you in many cases end up having to rebuild your CSS, and that is a pain to somehow remember …

1 Like

Hi, I’ve just recently thought about this myself, and I’ve basically tried to use Hugo + Tailwind without npm.

The styles are included like this:

{{ $styles := resources.Get "css/style.css" | minify | fingerprint }}

And at the root of the site, there’s a tailwind.config.js with these settings (for this site, I needed HTML blocks in markdown, so we’re scanning markdown files too :slight_smile:

module.exports = {
  content: [
    './layouts/**/*.{html,js}', // Paths to your template HTML files
    './content/**/*.{md,html}', // Paths to your Markdown files
    // Add any other paths that might contain Tailwind classes
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

But I ran into a problem since I’m doing all of this in VS Code, and it turned out I had to run two terminals (as bep mentioned). On Windows, this can be solved by creating an instruction file for PowerShell. I have this go.ps1 file with the following content:

# Start Tailwind CLI
$tailwind = Start-Process tailwindcli -ArgumentList '-i assets/css/input.css -o assets/css/style.css --watch --minify' -PassThru -NoNewWindow

# Start Hugo
$hugo = Start-Process hugo -ArgumentList 'server --disableFastRender --cleanDestinationDir --printPathWarnings' -PassThru -NoNewWindow

# Handle interrupt signal
try {
    # Wait for processes to finish
    Wait-Process -Id $tailwind.Id, $hugo.Id
} catch {
    # Stop processes in case of interruption
    Stop-Process -Id $tailwind.Id
    Stop-Process -Id $hugo.Id
}

I run this entire setup from the VS Code terminal with the command
.\go.ps1

As a result, I have both applications running in one terminal window. :smiley:
It basically works, though I’m not sure how I can install Tailwind’s prose plugin yet, as I haven’t tried it, but so far everything runs fast and without the resource-heavy npm. If there are any simpler solutions for setup, I’d be glad to hear them.

For now, this option suits me better than using npm. Before, I had to find a special version here (thanks to @bep for that) and spend a little more time. But I’m happy to hear from someone else who’s thought about this too!

nods

I’m running with my initial gut. @bep is right, it is ultimately a better workflow using the integration :smile:

If anyone decides to pursue this crazy idea and encounters the issue of not being able to install tailwindcss typography (prose), it turned out to be easy. Here’s what we do:

npm install -D @tailwindcss/typography

Then, in input.css, after
@import "tailwindcss";
insert
@import "@tailwindcss/typography";

or
@plugin "@tailwindcss/typography";

Both ways work! Honestly, I was already planning to switch to the starter version GitHub - bep/hugo-starter-tailwind-basic: A basic and simple to set up Hugo with TailwindCSS starter project., but it turned out that prose works, so I’m sticking with Tailwind CLI. :zany_face:

For anyone stumbling across this, I strongly encourage you to follow the documented setup:

https://gohugo.io/functions/css/tailwindcss/#setup

There’s a reason we documented it this way.

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.