Build, Minify, and Fingerprint SCSS with Hugo Pipes
For a small Hugo site, I would not add a JavaScript build tool just to compile one SCSS entry point. Hugo Pipes can compile it, create development source maps, compress production output, and add a content hash.
Start with the source and target paths
Put the source file under Hugo’s assets directory, then define its path and the published target:
{{- $cssSource := "scss/main.scss" }}
{{- $cssTarget := "css/style.css" }}
Keep source maps out of production
Use different options for the local server and production build:
{{- $cssOptions := cond (.Site.IsServer) (dict "targetPath" $cssTarget "enableSourceMap" true) (dict "targetPath" $cssTarget "outputStyle" "compressed") }}
When .Site.IsServer is true, Hugo enables a source map. Otherwise it produces compressed CSS.
Fetch the asset and pass it through toCSS:
{{- $style := resources.Get $cssSource | toCSS $cssOptions }}
Then load the generated permalink in the template:
<link rel="stylesheet" href="{{ $style.RelPermalink }}">
The rendered HTML looks roughly like this:
<link rel="stylesheet" href="/css/style.css">
Fingerprinting also solves cache invalidation
Add fingerprint after compilation and output the generated integrity value:
{{- $style := resources.Get $cssSource | toCSS $cssOptions | fingerprint }}
<link rel="stylesheet" href="{{ $style.RelPermalink }}" integrity="{{ $style.Data.Integrity }}">
This will output something like:
<link rel="stylesheet" href="/css/style.9eb7a4aa85871fc4fcf75373921fe7a57fad0b4a7e698cd5861eddfb5ba4584ab7e5.css" integrity="sha256-nrekqoWHH8T891Mf56V/rQtKfmmM1YYYYe3ftbpFhKt+U=">
The hash in the filename changes whenever the CSS changes, so long-lived browser caches can keep old files without hiding a new release. The integrity attribute also lets the browser verify that the downloaded content matches the expected hash.
Keep one entry point, not one giant file
SCSS partials can still separate variables, mixins, components, and layouts:
// main.scss
@import "variables";
@import "mixins";
@import "components/buttons";
@import "layouts/header";
Hugo Pipes also handles other assets. A JavaScript file can be minified and fingerprinted with the same pattern:
{{- $js := resources.Get "js/main.js" | minify | fingerprint }}
<script src="{{ $js.RelPermalink }}" integrity="{{ $js.Data.Integrity }}"></script>
This is enough for many Hugo sites. If the project needs a large JavaScript toolchain, framework-specific transforms, or plugins Hugo does not support, a separate build system may still be justified. I would add it for those requirements, not by default.