Sassy dark mode
Backward compatible dark mode
/i\ Now with Dart Sass version.
The easiest way to implement dark mode these days is with CSS3 custom properties (variables). Just have a :root element with default colors, and then another one under a media query, and let the user preference do the work. Something like this:
:root { --bkg: white; --txt: black; } @media ( prefers-color-scheme: dark ) { :root { --bkg: black; --txt: white; } } body { background: var(--bkg); color: var(--txt); }
And if the browser doesn’t understand or have the preference, it would take the first set of variables. But if it doesn’t understand variables at all, that’s a problem.
Dart Sass
Hugo uses LibSass, which will eventually become deprecated, so I figured out how to do it in Dart Sass. The old version is still available.
In a way, it’s easier. We will use one file _theme.scss. In it, we are going to declare that we will use a map, create two maps for two color schemes, and make a mixin of our style, which we will paste twice: once as a fallback default and once for a specific color theme preference. So, we want something like this:
@use "sass:map"; $light-theme-map: ( "background": white, "color": black, ); $dark-theme-map: ( "background": black, "color": white, ); @mixin mixin-colors( $our-theme ) { body { background: map.get( $our-theme, "background" ); color: map.get( $our-theme, "color" ); } } @include mixin-colors( $light-theme-map ); @media screen and ( prefers-color-scheme: dark ) { @include mixin-colors( $dark-theme-map ); }
Looking from the bottom, we see two @include’s with one variable each. One is a fallback with $light-theme-map that would be displayed in very old browsers, and the other is specific with $dark-theme-map.
They both pull in our main thing, which is a @mixin called mixin-colors() (original, I know), that we have higher up. This is our main CSS for all color styles. It is in there, where we are getting colors from maps with map.get( $map, "key" ).
Lastly (almost), we have two maps with colors for light and dark themes. They are more or less self-explanatory and resemble other variables, but they have slightly different syntax (to confuse me).
This approach has one drawback. As far as I understand, you can put a variable inside a map and manipulate it as usual. However, you can’t retrieve that variable from the map itself. In other words, you can’t use map.get() on the same map. So if I want to use one color to morph into other colors, I should first declare it as a variable and then pull derivatives into the map.
And the actuall last thing is declaring that we will use a map: @use "sass:map";. Then, we simply use our _theme.scss file in our main style.scss file by putting @use "theme"; at the top of it, and that’s it.
We could put our mixins in a different spot, and do all sorts of other fancy things, but that is outside the scope of this post. To be frank, I still don’t use Sass that much outside of this case.
LibSass
I really hope there is a better solution, because mine feels very silly, but it works. Sass variables. Bare minumum primer: Sass is a preprocessor, it creates a regular CSS file and all the magic happens before that. Also, there’s a pain point if you’re adapting an existing stylesheet: it’s better if the color information has its own section. Lucky for me, I have been doing this since time immemorial.
Ok, to get to the point, we need 6 files. Did I mention it’s silly? style.scss is our main file, which would be our actual style.css at the end. _colors.scss is our color information where we will write it with our variables. Two files for the light and dark variables themselves and two to make a magic trick work.
So, the magic trick is this: we import two files into our main file, and both of those files import a set of variables and our colors file:
_vars_light.scss + _colors.scss → _magic_light.scss
_vars_dark.scss + _colors.scss → _magic_dark.scss
_magic_light.scss + _magic_dark.scss → style.scss
So style.scss would have this:
@import "magic_light"; @media ( prefers-color-scheme: dark ) { @import "magic_dark"; }
_magic_light.scss would have this:
@import "vars_light"; @import "colors";
_vars_light.scss would have this:
$bkg: white; $txt: black;
And _colors.scss would have this:
body { background: $bkg; color: $txt; }
_magic_dark.scss and _vars_dark.scss would “mirror” _magic_light.scss and _vars_light.scss.
BTW, there may be different requirements for how Sass files are handled, I just go by what Hugo does with them and what works for me.
In the end, we have a rather large style.css file with a default light style and a dark style under a media query. Kind of like what we did with CSS3 variables, but that works without them. Silly!
A bit more about Sass.







