Implementing a Dark Theme for your web site using CSS Media Queries

Written by Paul Bradley

Support your users preference to use a dark theme by implementing a dark theme colour scheme using CSS Media Queries

midjourney ai - A samurai with pitch black skin standing in front of a deserted city

Table of Contents
  1. Introduction
  2. Determining if enough users prefer a dark theme
  3. Implementing a dark theme
  4. Handling Images

Introduction

All the major web browsers have a feature that allows users to choose their preferred colour scheme for web sites. Users can choose between automatic, light or dark themes.

If a user prefers to have a dark theme then as web developers we should honour their preference and adjust our sites colour scheme to match. This article covers explains how to use CSS media queries to implement a dark theme.

Determining if enough users prefer a dark theme

You might decide you want to monitor how many of your visitor/users have a preference for a dark theme, before committing to implementing one.

The CSS (Cascading Style Sheet) snippet below uses @supports to check if the visitors browser supports colour schemes. If the browser does support colour schemes then it uses a media query to see if prefers-color-scheme is set to dark.

1@supports (color-scheme: dark light) {
2    @media screen and (prefers-color-scheme: dark) {
3        body {
4            background-image: url('https://paulbradley.dev/img/darktheme.svg');
5        }
6    }
7}

If the dark preference is set then add a background image to the document body. The file darktheme.svg is a transparent SVG file which makes no visual difference to the website, but as the file is being requested from the web server it will show up within your web logs. Using your log analyzer of choice, I prefer GoAccess, you’ll be able to see how many people have dark theme as their preference.

The content of the darktheme.svg file is the minimal amount of code required to be a valid SVG file.

1<svg xmlns='http://www.w3.org/2000/svg'/>

The SVG example was taken from this cool repository which aims to collect the smallest possible syntactically valid files in different programming/scripting/markup languages.

Implementing a dark theme

To implement a dark theme your going to be focussing on redefining the colour used within your web pages. At the very least you’ll need to change the background and foreground colours of the body tag. You’ll also want to change the colour of all the links within your pages.

Like in the snippet below for the class .banner, you might decide the page element doesn’t transition well to a dark theme and choose to hide it by switching the display property to none.

 1@supports (color-scheme: dark light) {
 2    @media screen and (prefers-color-scheme: dark) {
 3
 4        .banner {
 5            display: none;
 6        }
 7
 8        body {
 9            background-color: #242323;
10            color: rgb(231, 233, 234);
11        }
12
13        a {
14            border-bottom:1px dotted rgb(29, 155, 240);
15            color:rgb(29, 155, 240);
16        }
17
18        a:visited {
19            color: rgb(46, 161, 237);
20        }
21
22        a:hover {
23            border-bottom:1px dotted rgb(231, 233, 234);
24            color:rgb(231, 233, 234)
25        }
26    }
27}

Handling Images

Images can pose a problem. If you’re able to produce different imagery for the different colour themes then you can use the picture tag to show the correct version of the image, see below for an example.

If you can’t retrospectively generate a dark theme suitable image then you can use the following CSS snippet to turn you light theme imagery into a greyscale version with a slight reduced opacity. This should make the images less glaring within a dark theme. These rules are applied to every image that isn’t an SVG file. So it’s applied to all my JPG and PNG images.

1img:not([src*=".svg"]) {
2    border: 2px solid rgb(113, 118, 123);
3    filter: grayscale(100%);
4    opacity: 90%;
5}

In my opinion piece, Using SQLite on EFS I took the time to prepare two versions of the architectural diagram. I’ll cover how I did that in another article.

The HTML snippet below shows how to use the picture tag to set different source images based on the media queries.

 1<picture>
 2    <source srcset="../svg/diagram-dark.svg"  media="(prefers-color-scheme: dark)" />
 3    <source srcset="../svg/diagram-light.svg" media="(prefers-color-scheme: light),
 4        (prefers-color-scheme: no-preference)" />
 5
 6    <img decoding="async"
 7         loading="lazy"
 8         width="655"
 9         height="543"
10         src="../svg/diagram-light.svg"
11         alt="AWS Architecture Diagram" />
12</picture>