Supporting Native Browser Dark Mode

11/12/2022 in code

#piranha-cms #darkmode #piranha #aspnet #bootstrap #css

When I decided to relaunch this site last year, I knew that I wanted to build it using bootstrap and .net 5+, both of which are provided out-of-the-box by Piranha CMS. I also decided that in order to save the eyes of readers like you, I needed to support browser dark mode natively. I've always been a fan of the bootstrap themes available from bootswatch.com, so my idea was to re-theme the entire site automatically, based on user dark mode preference.

Compile distinct CSS files

In order to setup dark mode on this site, I began by downloading the sass files for my chosen themes, Flatly and Darkly, so that they could be compiled along with the default Piranha CMS sass files. After copying the sass files into my site and organizing accordingly, I wired up the minification and output task in my gulpfile.js, so that the end result would be distinct, minified css files in my wwwroot/assets/css folder.

gulp.task('min', function (done) {
    gulp.src('assets/scss/darkly.scss')
        .pipe(sass().on('error', sass.logError))
        .pipe(cssmin())
        .pipe(rename({
            suffix: ".min"
        }))
        .pipe(gulp.dest('wwwroot/assets/css'));

    gulp.src('assets/scss/flatly.scss')
        .pipe(sass().on('error', sass.logError))
        .pipe(cssmin())
        .pipe(rename({
            suffix: ".min"
        }))
        .pipe(gulp.dest('wwwroot/assets/css'));
    done();
});

Dim images in dark mode

One issue I have with most browser dark mode implementations is that images are incredibly bright, especially those with a lot of white pixels. In order to be a little more kind to my readers, I thought it would be appropriate to slightly dim images in dark mode. To achieve this, I added the following snippet to the _darkly.boostwatch.scss file:

@media (prefers-color-scheme: dark) {
  img:not([src*=".svg"]) {
    filter: brightness(.8) contrast(1.2);
    transition: all .5s ease-in-out;
    &:hover {
      filter: none;
    }
  }

  img.brand-icon {
    filter: none !important;
  }
}

The code above uses a media query to set an image filter, which slightly decreases brightness and increases contrast on images site-wide. I also found that my brand icon was negatively impacted by this, so I excluded it explicitly, as well as all SVG files.

Reference distinct CSS files

All that remained to complete my setup was to reference the distinct CSS files in the head section of my site. Because Piranha CMS is an ASP.NET site, I added the following snippet to the _Layout.cshtml file:

<head>
...
<meta name="color-scheme" content="light dark">
<link rel="stylesheet" href="~/assets/css/darkly.min.css" media="(prefers-color-scheme: dark)">
<link rel="stylesheet" href="~/assets/css/flatly.min.css" media="(prefers-color-scheme: no-preference), (prefers-color-scheme: light)">
...
</head>

The first line in the snippet above defines the ability of this site to support light and dark modes natively. The next two lines define the distinct CSS file to use, based on a media query that captures the user color scheme preference. Here, I define the Darkly theme specifically when a user prefers dark mode, otherwise a user preference of light OR no preference set, results in the flatly theme being applied. As you can see below, applying each theme, with the relevant enhancements for images, results in a much friendlier dark mode.