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.

Let’s be clear about what this is. Hugo doesn’t bundle a full Sass compiler. Instead, it acts as a client and speaks to a separate, standalone Dart Sass Embedded binary over a protocol buffer interface. Think of it like a master chef (Hugo) who has a dedicated saucier (the Dart Sass binary) working in perfect harmony. The chef shouts “béarnaise!” and the saucier hands over the perfect sauce without the chef having to own every single butter churn and egg whisk. This separation is why it’s so fast and reliable.

Grabbing the Binary

First, you need that saucier. Head over to the GitHub releases for the Dart Sass Embedded page. This is the only part where you have to do a manual download. Find the appropriate binary for your operating system (e.g., sass_embedded-{version}-windows-x64.zip for Windows, -linux-x64.tar.gz for Linux, -macos-x64.tar.gz for Intel Macs, -macos-arm64.tar.gz for Apple Silicon). Download it, extract it, and plop the binary file (on Windows it’s sass_embedded.bat, on others it’s just dart-sass-embedded) somewhere sensible.

The key is to either:

  1. Put it in your system’s PATH so Hugo can find it automatically (the easiest way).
  2. Or, tell Hugo exactly where it lives using the SASS_PATH environment variable.

Option 1 is best. On macOS or Linux, dropping it into /usr/local/bin is the classic move. On Windows, add its folder to your User or System PATH. Verify it works by opening a new terminal and typing dart-sass-embedded --version. If it cheerfully reports its version number, you’re golden.

Configuring Hugo to Use It

Hugo, being the sensible tool it is, will automatically use the Dart Sass Embedded binary if it finds it in the PATH. But let’s not leave things to chance. You can explicitly enable it in your hugo.toml (or config.toml):

[build]
useResourceCacheWhen = 'fallback'

[module]
[[module.mounts]]
source = 'assets'
target = 'assets'
[[module.mounts]]
source = 'src/assets'
target = 'assets'

# This is the magic
[build.buildStats]
enable = true

Wait, where’s the Dart Sass setting? Ah, here’s the beautiful part: you don’t configure it here. The [build.buildStats] section lets Hugo build a dependency graph of your assets, which is crucial for its caching intelligence. The actual use of the external Sass compiler is implied and automatic once the binary is available. Hugo will prioritize it over the legacy LibSASS compiler every time.

Writing Your SCSS for Hugo

Now for the fun part. Your SCSS files live in your assets directory (e.g., assets/scss/main.scss). Hugo will pick them up, process them through the embedded Dart Sass binary, and output a CSS file directly into your public/ folder. The critical step is to reference the output file in your template’s <head>.

Here’s a typical assets/scss/main.scss:

// Import your partials - Hugo and Dart Sass handle this perfectly
@import 'variables';
@import 'components/card';

// Now, let's party with some variables and nesting
$primary: #3498db;

body {
  font-family: sans-serif;
  color: #333;

  .header {
    background-color: $primary;

    a {
      color: white;
      text-decoration: none;

      &:hover {
        text-decoration: underline;
      }
    }
  }
}

And in your template (e.g., layouts/partials/head/css.html):

{{- with resources.Get "scss/main.scss" | toCSS | minify | fingerprint }}
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
{{- end }}

This pipeline is glorious: resources.Get fetches the file, toCSS compiles it using our embedded binary, minify crushes it, and fingerprint adds a hash for cache-busting. You get perfectly processed, modern SCSS without a single node module.

Common Pitfalls and the “Gotcha”

The biggest “gotcha” is forgetting that the binary is a separate entity. If you update Hugo, you don’t automatically get an updated Sass binary. You are now your own package manager for this one tool. When a new, must-have Sass feature drops, you’ll need to manually download the new version. It’s a trade-off: infinite stability and simplicity for a tiny bit of manual dependency management. Frankly, it’s a trade I’ll make any day of the week.

Another hiccup can be PATH issues. If Hugo can’t find the binary, it will fall back to its legacy LibSASS support, which might not support the modern SCSS syntax you’re using. The tell-tale sign is an obscure error. If you see this, your first troubleshooting step should always be to open a terminal and run dart-sass-embedded --version. If that command isn’t found, Hugo can’t find it either. Fix your PATH.

This approach is the professional’s choice for Hugo projects. It leverages the best parts of the ecosystem (modern Sass features, blistering speed) while utterly rejecting the worst parts (node dependency hell). You get to keep your project directory clean, your builds fast, and your sanity intact. Now go build something.