Even relatively simple websites can benefit from Sass, to help make their CSS files more structured and less repetitive.
The extended releases of Hugo (those with _extended_
in their names!) come bundled with LibSass, a Sass-to-CSS transpiler wrapper.
So, before anything else, make sure you are running one of these “extended” versions of Hugo. Run the hugo version
command, at the command line, and check for “extended” in the version string, shown in the output.
(You can also use a more modern Sass transpiler - Dart Sass) with Hugo, by following these instructions. But LibSass is fine for the purposes of this article.)
The following code transpiles a sass file to a css file. You would typically place this code wherever you need to access the css file - for example in your Hugo template containing <head>...</head>
content.
This is probably in layouts/partials/head.html
- but your set-up may be different.
|
|
As with all things Hugo, there are various different ways you can arrange your code and related files. But here is what I did for the above example - a very basic setup:
assets/scss/demo.scss
(creating the scss/
directory along the way).The file contains this sass:
|
|
This is the same sass example that you can find in the official documentation here.
The expected CSS (after transpiling) will eventually be this:
|
|
I added the above Hugo code to my head.html
partial.
I built my test site using the hugo server
command. This defaults to using a Hugo environment of development
.
The code needs some explaining…
|
|
The with
command takes the result of the rest of the expression and assigns it to the current context (Hugo’s .
). This also has the property of silently skipping the nested commands if the rest of the expression fails (e.g. if the demo.scss
file is not found). No error is thrown, in that case.
The resources.Get "scss/demo.scss"
command only looks in the assets
directory. Yes, there is a separate resources
directory, but that is not where .Get
looks, by default.
|
|
The above command creates a Hugo variable $opts
. It populates that variable with a dict
- a dictionary strcture consisting of a collection of key-value items. These are options that we will be passing to the sass transpiler.
I have laid out this command over multiple lines (which is legal syntax for Hugo), to help make it easier to read the key-value pairs.
For example, we assign the value libsass
to the key transpiler
. This is actually the default value used by Hugo if you do not use any options at all… but I prefer to be explicit here. You would use dartsass
instead if you had installed the Dart transpiler.
The expanded
output style matches the sample CSS I show above. You can always look at the official sass documentation to explore this and other options.
The target path of css/demo.css
defaults to the location where Hugo places all the built website files when you run the hugo server
command. So, in my case this means the final CSS file is placed in public/css/
.
static/css/
. But it is actually created in public/css/
.
Now we use our options in the next line of code:
|
|
This takes the current context (.
) - which as noted already is the SCSS resource (the file) we got using .get
.
We pipe (|
) this file through the toCSS
function provided by Hugo. And we provide that toCSS
function with our dictionary of parameters. This is what actually converts the scss file to css.
Note the difference between the
|
operator (which is a Go “pipe” connecting the output of one command into the input of the next command) and our topic of Hugo pipes - which are a different thing altogether. The above command uses both!
As before, the with
in the above command means we take the resulting CSS output and make that the Hugo context for the next nested Hugo commands inside this with
structure.
|
|
Unless we change Hugo’s command line defaults, the above test will be true
when we run our site using hugo server
for development and testing; but it will be false
when we build the production-ready site using the hugo
command.
For the case where .IsDevelopment
is true
, we build this piece of HTML:
|
|
And that is simply rendered as:
|
|
And there we have our stylesheet link in our <head>...</head>
tag.
The above command does one extra crucial thing for us, automatically. It triggers creation of the CSS file which gets placed where we wanted (as per the configuration options we provided) - and with the name we specified:
"targetPath" "css/demo.css"
This is covered (somewhat tersely) in the Hugo docs, here:
Hugo publishes assets to the
publishDir
(typicallypublic
) when you invoke.Permalink
,.RelPermalink
, or.Publish
.
So our Hugo sass pipeline is triggered by our need to build that href="{{ .RelPermalink }}"
. The end result is:
(1) a new CSS file in public/css/demo.css
(2) the stylesheet link in our page’s <head>
.
For the case where we are building the production
version of our website, using the hugo
command, we do this instead:
|
|
It’s basically the same overall process as for the development
steps, but with an extra minify
action and then a fingerprint
action.
We use the results of these chained commands to generate a different CSS file - one which has been minified, and which has a fingerprint hash added to the file name - something like this (shortened for display purposes here):
demo.min.40b5dfdc306b472b6...9ce2bd15cdd080e.css
And the stylesheet link reflects the <link>
attributes we specified - similar to this (again, simplified here for display purposes):
|
|
There’s a lot going on there - with some very important steps happening automatically.
There are many different (and many more sophisticated) ways to use Hugo pipes. This is just one very basic example, to explain the fundamentals.
And a final note: You may have noticed that there is also a Hugo resources
directory in your project. This is used by Hugo pipes when processing resources (the ones you placed in that other assets
directory…).
You can take a look at what’s in there, if you are curious. Hugo explains the directories like this:
The
assets
directory contains global resources typically passed through an asset pipeline. This includes resources such as images, CSS, Sass, JavaScript, and TypeScript.
The
resources
directory contains cached output from Hugo’s asset pipelines, generated when you run thehugo
orhugo server
commands. By default this cache directory includes CSS and images. Hugo recreates this directory and its content as needed.
This raises another very important point about caching:
The pipe chain is only invoked the first time it is encountered in a site build, and results are otherwise loaded from cache.
You can delete this cached output during development if you need to start afresh and clear out old, unwanted assets.