Quick notes on variables, dart-sass and hugo

Background

Recently I decided that it was annoying for me as a user to not know which links on my site were internal and which led off to an external site.

Figure 1: The desired end-result, blue for internal links, purple for external links, colors chosen with ColorHexa

Figure 1: The desired end-result, blue for internal links, purple for external links, colors chosen with ColorHexa

However, since hugo requires baseurl for generating meaningful rss and sitemap files, this meant either hardcoding the variable into my theme, or figuring out how to pass variables from my config.toml to my theme’s (generated) css.

This about the latter, and is heavily based off of this post on the Hugo Discourse.

The CSS

The crux of the implementation is simply the use of attribute selectors:

 1a {
 2  &[href^="/"],
 3  &[href*="rgoswami.me"],
 4  &[href^="#"] {
 5    border-bottom: #0380ff 0.125em solid;
 6  }
 7  &:not([href^="/"]):not([href*="rgoswami.me"]):not([href^="#"]) {
 8    border-bottom: #9b36ff 0.125em solid;
 9  }
10}

Where the main sticking point is being able to pass the name of the site from the configuration. Although this could be done with libsass and hugo as detailed in the discourse post, it is conceptually clunky, since partials are not respected when using ExecuteAsTemplate with hugo-pipes. A better SCSS processor could probably take in a better configuration option object…. Which leads to the next section.

Dart Sass

At some point which I didn’t have time to figure out, libsass got phased out in favor of Dart Sass and includes (among other features) a nifty math module. This had a few side effects:

  • I could swap my hand-kanged exp function for math.exp() and also had to transition to using calc() more (as in 3938ac2).
  • I could pass variables from hugo to scss and from there to css for attribute selectors (as in 804d417)

CI Updates

For various reasons, hugo (even the extended version) does not seem to include dart-sass-embedded though the snap package does (as noted here on Discourse).

1- name: Setup Hugo
2  uses: peaceiris/actions-hugo@v2
3  with:
4    hugo-version: '0.111.3'
5    extended: true
6- name: Install Dart Sass Embedded
7  run: sudo snap install dart-sass-embedded

Local Usage

hugo serve will correctly compile and load the theme if hugo env recognizes the dart-sass-embedded installation:

 1$ hugo env
 2hugo v0.112.3+extended linux/amd64 BuildDate=unknown
 3GOOS="linux"
 4GOARCH="amd64"
 5GOVERSION="go1.20.4"
 6github.com/sass/libsass="3.6.5"
 7github.com/webmproject/libwebp="v1.2.4"
 8github.com/sass/dart-sass-embedded/protocol="1.2.0"
 9github.com/sass/dart-sass-embedded/compiler="1.54.8"
10github.com/sass/dart-sass-embedded/implementation="1.54.8"

dart-sass-embedded is a node package, which means that npm can be used:

1DART_SASS_VERSION=1.54.8 npm install sass-embedded-linux-x64@${DART_SASS_VERSION}
2PATH=$PATH:./node_modules/sass-embedded-linux-x64/dart-sass-embedded/ hugo env

Personally I ended up using micromamba to grab dart-sass as well.

Passing Variables

User Perspective

The theme users simply updates their configuration to include the relevant variables:

1baseurl = "https://rgoswami.me/"
2[params.style]
3site_url = "rgoswami.me" # Same as baseurl

Theme Dev Perspective

With the hugo asset processing pipeline for dart-sass, the relevant options need to be (minimally) set:

1{{ $options := (dict "targetPath" "main.css" "transpiler" "dartsass" "vars" site.Params.style) }}
2{{ $style := resources.Get "scss/main.scss" |  resources.ToCSS $options | resources.Minify | resources.Fingerprint }}
3<link rel="stylesheet" href="{{ $style.RelPermalink }}">

Where vars takes a section corresponding to the user-defined section (style in this case). Usage within scss partials looks like:

1@use "hugo:vars" as h;
2$site-url: h.$site_url;
3$link-color: #0380ff;
4$external-link-color: #9b36ff;

Which can finally be used to elegantly setup attribute selector based colors:

 1a {
 2  &[href^="/"],
 3  &[href*="#{$site-url}"],
 4  &[href^="#"] {
 5    border-bottom: $link-color 0.125em solid;
 6  }
 7  &:not([href^="/"]):not([href*="#{$site-url}"]):not([href^="#"]) {
 8    border-bottom: $external-link-color 0.125em solid;
 9  }
10}

Conclusions

Of the many maintenance burdens I have accrued over the years, one of the least often discussed is the underlying theme used for the site. Some day that will be rectified. I think web design via vanilla JS, S(CSS) and HTML is a pretty nifty gateway into other languages (interpreted and compiled). The approach could be (and perhaps should be) extended to color other links and sites differently (e.g. all my content on other sites in blue). For now this works well enough at any rate.