26.7 Avoiding the npm Dependency: Dart Sass Embedded

Right, so you want to build a Hugo site with SCSS and Tailwind, but the thought of wrestling with a sprawling node_modules directory and a fragile package.json makes you twitch. I get it. You came to Hugo for its simplicity and speed, not to recreate the frontend plumbing of a Fortune 500 company’s web app. The good news is, Hugo’s got your back. It has a brilliant, built-in secret weapon for processing your SCSS without ever needing to type npm install: the Dart Sass Embedded protocol.

26.6 Dark Mode with Tailwind and Hugo

Right, dark mode. It’s 2024, and we’re still pretending this is a fancy new feature. Let’s be honest: your users want it, your retinas need it, and implementing it in a static site generator like Hugo with Tailwind CSS is actually a joy once you stop fighting the tools and start letting them do the work for you. The core idea is simple: we toggle a class (usually .dark) on a root element like <html> or <body>, and Tailwind’s dark: variant does the rest, swapping your color utilities based on that class. The real trick, the part that separates a pro setup from a hacky one, is how you handle the user’s preference, persist their choice, and avoid that awful flash of un-styled content (FOUC) on load.

26.5 Purging Unused Tailwind Classes with content Paths

Right, let’s talk about the single most important, and frankly, most annoying, part of using Tailwind with Hugo: purging. You’ve fallen in love with Tailwind’s utility-first speed, but if you just ship the entire 4MB+ of the development build to your users, you’re a monster. A well-intentioned monster, but a monster nonetheless. The purge option in Tailwind is our salvation, our robot vacuum that goes around and only picks up the classes we’ve actually used. But you have to tell it where to look, and with Hugo, that’s a bit of a dance.

26.4 Setting Up Tailwind CSS with Hugo

Right, let’s get our hands dirty. You’re about to make Hugo and Tailwind play nice together, which is a fantastic idea. You get the component-driven, content-focused power of Hugo and the rapid, utility-first styling of Tailwind. But their default setups are like two brilliant people who speak different languages; we need to build a solid interpreter between them. We’re going to process your Tailwind CSS on the fly, as part of Hugo’s build process. This is the way.

26.3 PostCSS Pipeline: postcss.config.js and npm Integration

Right, so you’ve decided to build a Hugo site and you want to use Tailwind CSS. Smart. You’ve probably hit the first roadblock: Hugo’s built-in SASS/SCSS support is great, but it’s a completely separate universe from the Node.js-based tooling Tailwind requires. You can’t just @import "tailwindcss"; and call it a day. This is where we stop fighting the tool and start making it work for us. We’re going to set up a PostCSS pipeline. Think of it as a bouncer for your CSS: every line of your stylesheet has to get past PostCSS and its friends before it’s allowed into the final, built site.

26.2 SCSS Variables, Nesting, Mixins, and Imports

Alright, let’s get our hands dirty. You’re using Hugo, which means you’re already a step ahead of the game because Hugo’s built-in Sass/SCSS compilation is fantastic. It means we can write clean, powerful, and, most importantly, DRY (Don’t Repeat Yourself) stylesheets and let Hugo handle the messy conversion to plain CSS for the browser. This is where SCSS becomes your best friend, and Tailwind is the quirky, super-efficient roommate who keeps things interesting.

26.1 SCSS Compilation with Hugo Extended: toCSS

Right, let’s talk about getting Hugo to turn your elegant SCSS into the blunt, browser-ready CSS it so desperately needs. This isn’t magic, it’s Hugo’s toCSS pipeline, and it’s the single most important reason you need the “Extended” version of Hugo. The regular version is a brilliant static site generator; the Extended version is that plus a full-fledged asset pipeline. Don’t even try this with the regular version. You’ll just get errors and a profound sense of disappointment, and I won’t feel bad for you.

25.8 The integrity Attribute for Subresource Integrity (SRI)

Right, let’s talk about making your site a fortress. You’ve gone through the trouble of setting up HTTPS, your headers are tight, and then you go and load a script from some third-party CDN. You’re trusting that CDN to serve the exact code you tested, not something a malicious actor slipped in there. That’s a huge, glaring weak spot. This is where Subresource Integrity (SRI) comes in, and Hugo, being the brilliant but occasionally obtuse tool it is, gives us the integrity attribute in its resources pipeline to handle it.

25.7 Concatenation: resources.Concat

Right, let’s talk about concatenation. You’re probably thinking, “Isn’t this just gluing files together?” And you’d be right. But in the world of static sites, doing this efficiently without a live build server (like Webpack) watching your every move is a bit of a superpower. That’s where resources.Concat comes in. It’s your go-to for bundling those pesky little CSS or JS files into a single, cache-friendly, HTTP-request-reducing masterpiece. Think of it as the static site equivalent of duct tape and ambition—but it actually works.

25.6 Minification: resources.Minify

Right, let’s talk about minification. You’ve probably heard the term thrown around like a holy mantra for performance. “Minify your assets!” they shout from the conference stages. And they’re not wrong. But what does it actually mean in Hugo? It means taking your beautifully formatted, human-readable CSS, JS, JSON, HTML, or SVG and ruthlessly stripping out every single unnecessary byte. We’re talking whitespace, comments, and sometimes even shortening variable names. It’s the digital equivalent of vacuum-packing your clothes for a trip. The goal isn’t to be pretty; it’s to be small and fast.

25.5 Fingerprinting and Cache-Busting: resources.Fingerprint

Right, let’s talk about cache-busting. It’s one of those problems that sounds trivial until you’ve spent an hour staring at a browser, hitting Ctrl+F5 until your keyboard begs for mercy, because your gorgeous new CSS file is stubbornly refusing to load. The browser is faithfully serving the old, cached version, convinced nothing has changed. We need a way to tell the browser, definitively, “This is a new file. I mean it this time.”

25.4 JavaScript Bundling with js.Build and esbuild

Alright, let’s talk about making your JavaScript not suck. You’ve got a pile of modern JS—maybe some TypeScript, ES6 modules, fancy dependencies—and you need to serve it to a browser that still probably thinks let is a typo. This is where js.Build and its engine, esbuild, come in. Think of js.Build as Hugo’s direct line to one of the fastest, most no-nonsense bundlers on the planet. It doesn’t mess around with a million plugins; it just gets the job done, brutally efficiently.

25.3 CSS Processing: toCSS, PostCSS, and autoprefixer

Right, let’s talk about making your CSS less of a mess and more of a… well, a slightly more organized mess that actually works across browsers. Hugo gives us a fantastic toolkit for this, and if you’re not using it, you’re essentially writing your stylesheets with a rock and a chisel. We’re going to cover the three big hitters: toCSS, PostCSS, and the lifesaver known as autoprefixer. First, the basics. You don’t just throw a regular .css file in your assets directory and call it a day. Instead, you process it. This usually means you’ll create a file with a special extension, like styles.css or, my personal favorite, something.scss (even if you’re not using Sass!). Why? Because this tells Hugo’s asset pipeline, “Hey, I need you to do something to this file before you serve it.”

25.2 resources.Get and resources.GetRemote

Right, let’s talk about getting stuff. In Hugo, your content isn’t just the markdown files you lovingly craft; it’s also every image, PDF, CSS file, and JSON blob your site needs. This is where resources.Get and resources.GetRemote come in. Think of them as your two best friends for asset acquisition: one for the local stuff you’ve already committed to your project (resources.Get), and one for the brave new world of the internet (resources.GetRemote). They’re the foundation of Hugo Pipes, and once you get them, you can make this static site generator do backflips.

25.1 What Hugo Pipes Are: Processing assets/ Files

Right, let’s talk about Hugo Pipes. Forget what you’ve heard about static site generators just slapping together pre-made files. Hugo Pipes is the reason I can, with a straight face, call Hugo a modern frontend build tool that happens to output static HTML. It’s the engine under the hood that takes your raw, un-minified, un-processed assets and transforms them into the optimized, production-ready CSS and JS you actually want to serve.

21.8 Publishing a Theme: Hugo Themes Gallery Requirements

Right, so you’ve built this beautiful, clever Hugo theme. It’s a masterpiece of semantic HTML and clever partials. You, my friend, are an artist. But art locked in a closet is just hoarding. It’s time to unleash your creation upon the world via the Hugo Themes Gallery. This isn’t just a matter of pushing to GitHub and calling it a day. Hugo has a… let’s call it a particular set of requirements. They’re not difficult, but they are non-negotiable, and missing one will leave your theme languishing in “draft” status forever.

21.7 Making a Theme Configurable with Params

Right, so you’ve built a theme. It looks sharp. But here’s the problem: it’s a dictatorship, not a democracy. You’ve hard-coded the accent color to that specific shade of electric teal you’re so fond of, and the site title is set in stone. What if your user wants… gasp… maroon? We’re not barbarians. We need to hand over the reins, but in a controlled, sensible way. That’s where Hugo’s Params come in. Think of them as the control panel for your theme, letting the user tweak things without ever having to touch a line of Go template code.

21.6 Dark Mode Toggle Pattern

Right, so you want a dark mode toggle. Not just a little switch that winks an eye and hopes for the best, but a proper, persistent, system-respecting one. We’ve all seen the janky versions—the ones that flashbang you at 2 AM or forget your preference the moment you reload. We’re not building that. We’re building the one that does it right, because frankly, the user experience here is embarrassingly easy to screw up. Let’s get it right on the first try.

21.5 JavaScript: Bundling with esbuild via Hugo Pipes

Right, so you’ve decided to build a Hugo theme. Good for you. You’ve got your HTML templated, your CSS is piping hot, and now you need some actual, functioning JavaScript. You could just slap a <script src="main.js"> in your head and call it a day, but then you’d be serving a massive, un-minified, ES6+ mess to every browser, including a Nokia phone running Opera Mini. We’re better than that. Enter Hugo Pipes and esbuild. This is where Hugo stops being a simple static site generator and starts feeling like a full-fledged build tool. The beauty of it is that you don’t need a separate package.json, node_modules, or a sprawling Webpack configuration that requires a blood sacrifice to maintain. Hugo handles it all internally, and it’s brilliantly fast.

21.4 CSS Architecture: Vanilla CSS, Tailwind, and SCSS Integration

Right, let’s talk strategy. You’re about to build a theme, which means you’re making decisions that will haunt you—or bless you—for the entire project. The way you structure your CSS isn’t just about writing styles; it’s about writing maintainable styles that won’t make you want to set your computer on fire in six months. We have three main players here: the purity of Vanilla CSS, the utility-first speed of Tailwind, and the programmatic power of SCSS. The good news is, you don’t have to choose just one. The better news is, if you mix them wrong, you’ll create a monster. Let’s get it right.

21.3 Building the Base Template

Right. You’ve decided to build a theme from scratch. Good for you. This is where you stop being a tourist and start being a citizen. You’ll get your hands dirty, you’ll make mistakes, and you’ll learn more in the next ten minutes than you would from installing fifty pre-made themes. The base template is the bedrock. It’s the single most important file in your theme. Get this right, and everything else slots into place. Get it wrong, and you’ll be chasing weird bugs for weeks.

21.2 theme.toml: Theme Metadata

Right, let’s talk about the theme.toml file. This is your theme’s handshake, its business card, and its legally binding contract with Hugo, all rolled into one. Get this wrong, and Hugo will either throw a fit or, worse, build your theme with a whimper instead of a bang. It’s a TOML file because, well, the Hugo team made a choice. I don’t question the cosmic reasoning; I just write the config.

21.1 Theme Directory Structure: layouts, static, assets, i18n

Right, let’s get our hands dirty. You’re building a theme from scratch, which means you’re about to become intimately familiar with a very specific directory structure. This isn’t arbitrary bureaucracy; it’s Hugo’s contract with you. You put things in the right place, and Hugo, being a deeply opinionated but brilliant curmudgeon, knows exactly what to do with them. Break the contract, and things just… stop working. Let’s look at the four key directories you’ll be living in.

— joke —

...