In last few years, CSS changed a lot. Many new features were added, such as Flexbox, Media Queries and animations, but other things, such as using some kind of variables or modules that would allow better organizing of source code are still not so great. This is where CSS preprocessors come into play. And since I always had great experience with Sass preprocessor, I decided to use if for this blog, written using Hakyll static site generator.

Unfortunately there’s no built-in support for Sass or any other CSS preprocessor in Hakyll, but adding it is pretty straightforward and doesn’t require much coding. In this blog post, I decided to sum up this process, both for future myself and for anyone else who’d try to solve the same problem.

1 Using hakyll-sass package

Surprisingly, we’re not the first ones who solve this issue, so there’s a hakyll-sass package, that uses the hsass under the hood and wraps its logic into functions ready to be used in Hakyll. Easiest way is to use the sassCompiler function to compile .sass files to .css. Personally I prefer to split my stylesheets into smaller .scss files, and then @import these into the one main.scss, which then compiles into single monolithic main.css. This can be done using below code.

match "assets/css/main.scss" $ do
  route $ setExtension "css"
  compile (fmap compressCss <$> sassCompiler)

1.1 Problems with hot reloading

Now try the above with hot reloading using stack exec site watch command. When you change the main.scss file, changes are detected, recompiled and new .css file is generated, so you see the changes in web browser after refreshing. However, when you edit one of the secondary files you @include into the main one, no changes are detected, until you restart the hot reloading, which is pretty annoying.

So what’s wrong? Using the match "assets/css/main.scss" rule, we tell Hakyll to watch changes on file main.scss. But when you edit any of the included file, Hakyll has no clue that it also needs to recompile the main one, to refresh the resulting .css. You might try to change the above rule to match "assets/css/**.scss", but this won’t help too, because when now you edit the included file, Hakyll still has no clue that it also needs to recompile the main.scss and it results to the same problem.

1.2 The solution

What we need to do is to tell Hakyll to also recompile the main.scss each time any other .scss file is changed. It took me some time to discover how to do this, but then I found this post from Hakyll author, where he shows exactly what we need:

scssDependency <- makePatternDependency "assets/css/**.scss"
rulesExtraDependencies [scssDependency]
  $ match "assets/css/main.scss"
  $ do
      route $ setExtension "css"
      compile (fmap compressCss <$> sassCompiler)

In above code, thanks to the rulesExtraDependencies function, we’re able to add extra Dependency for the compiler we use to compile the main.scss. And to create it we use the makePatternDependency function that creates dependency from given Pattern, in our case for assets/css/**.scss.