The simplest CSS variable dark mode theme
Tags: css, javascript, webdev, tutorial
Published: 2020-08-10 12:29:17

---
title: The simplest CSS variable dark mode theme
published: true
date: 2020-07-26 10:50:26 UTC
tags: css,javascript,webdev,tutorial
canonical_url: https://lukelowrey.com/css-variable-theme-switcher/
---

 ![The simplest CSS variable dark mode theme](https://lukelowrey.com/content/images/2020/07/dark-mode.png)

I have been working on a [custom Ghost theme](https://lukelowrey.com/ghostsolo-a-free-and-open-source-ghost-theme/) to power my blog (you are looking at an early version right now!). One thing I wanted to have a crack at was a dark/light theme switcher. It turns out with modern CSS this is pretty straight forward.

The approaches I considered were:

- CSS classes set on the <body>
- Switch out style sheet entirely
- CSS variables

I went with CSS variables because my blog audience tends to be on the latest browser versions so I don't need to worry much about browser support ([not that it is too bad](https://caniuse.com/#feat=css-variables)).

If a blog post is too much for you, check out dark mode in 4 tweets thread.
{% twitter 1289427747396177921 %}

### Using CSS variables for themes

[CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) are properties you can set in your stylesheets that can be used in later styles. They are similar to tools such as SASS but built directly into CSS. This makes them very well suited for theming.

This is a simplified snippet from the default variables used in my theme [GhostSolo](https://lukelowrey.com/ghostsolo).

<!--kg-card-begin: markdown-->
```
:root {
    --background-color: #fff;
    --text-color: #121416d8;
    --link-color: #543fd7;
}

html[data-theme='light'] {
    --background-color: #fff;
    --text-color: #121416d8;
    --link-color: #543fd7;
}

html[data-theme='dark'] {
    --background-color: #212a2e;
    --text-color: #F7F8F8;
    --link-color: #828fff;
}

```
The :root selector is the default set of values. When _<html data-theme='dark'>_ is set those values are overridden by the _html[data-theme='dark']_ values. It is really just a matter of applying the variables in your CSS to get the effect.

```
body {
    background: var(--background-color);
    color: var(--text-color);
}

a {
    color: var(--link-color);
}

a:hover {
    text-decoration: underline;
    filter: brightness(80%);
}

```
<!--kg-card-end: markdown-->

In my theme I have variables for a couple of key style choices to allow distinct colour themes beyond dark/light.

<!--kg-card-begin: markdown-->
```
:root {
    --background-color: #fff;
    --alternate-background-color: #f7f7f9;
    --text-color: #121416d8;
    --text-color-light: #777676bb;
    --link-color: #543fd7;
    --masthead-color: #543fd7;
    --masthead-text: #fff;
    --button-color: #263238;
    --button-text: #fff;
    --bs-font-sans-serif: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    --bs-font-serif: Georgia, serif;
    --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}

```
<!--kg-card-end: markdown-->

> Note: the --bs prefixed variables are [CSS variables bootstrap 5 supports](https://v5.getbootstrap.com/docs/5.0/customize/css-variables/). The css variable support in Bootstrap is a bit limited and doesn't apply to things like _.btn-primary_ or _.bg-light_.

### JavaScript dark mode toggle

With the CSS theming in place I needed a simple way to switch between "dark" and "light" modes.

<!--kg-card-begin: markdown-->
```
var toggle = document.getElementById("theme-toggle");

var storedTheme = localStorage.getItem('theme') || (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
if (storedTheme)
    document.documentElement.setAttribute('data-theme', storedTheme)

toggle.onclick = function() {
    var currentTheme = document.documentElement.getAttribute("data-theme");
    var targetTheme = "light";

    if (currentTheme === "light") {
        targetTheme = "dark";
    }

    document.documentElement.setAttribute('data-theme', targetTheme)
    localStorage.setItem('theme', targetTheme);
};

```
<!--kg-card-end: markdown-->

This simple JavaScript snippet will set the theme based off the user's system preferred setting using a [media query for](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme)_["prefers-color-scheme: dark"](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme)._ It will also store the value in local storage so it can be persisted across each page. The selected theme is set as a data-theme attribute on the html node.

### Other CSS theming tricks

I couldn't quite get form inputs looking nice with a pure CSS variable approach. When using data attributes, you can always target them in your CSS the traditional way. This CSS gives the inputs a nice dark overlay against any background colour but didn't look right on the light theme.

<!--kg-card-begin: markdown-->
```
[data-theme='dark'] .form-control {
    background-color: rgba(0, 0, 0, 0.6);
    border-color: rgba(0, 0, 0, 0.6);
    color: var(--text-color) !important;
}

[data-theme='dark'] .form-control:focus {
    color: var(--text-color) !important;
}

```
<!--kg-card-end: markdown-->

I needed to show/hide my sun moon icons in the dark mode toggle button. I went with a Bootstrap inspired display utility approach to change an element display based on the theme.

<!--kg-card-begin: markdown-->
```
[data-theme='light'] .d-block-light,
[data-theme='dark'] .d-block-dark {
    display: block !important;
}


<button id="theme-toggle" class="btn btn-link btn-sm ml-2 small" type="button">
 <span class="d-block-light d-none">{{> "icons/moon"}}</span>
 <span class="d-block-dark d-none">{{> "icons/sun"}}</span>
</button>

```
<!--kg-card-end: markdown-->
### See it in action

Use the sun/moon icon in the nav bar of this page to see it in action. Or just watch the gif:

![The simplest CSS variable dark mode theme](https://lukelowrey.com/content/images/2020/07/theme-toggle.gif)<figcaption>Dark mode toggle</figcaption>

### References

- [Dark theme in a day - Marcin Wichary](https://medium.com/@mwichary/dark-theme-in-a-day-3518dde2955a) - some great advanced techniques with HSL colours and transitions
- [A complete guide to dark mode - CSS tricks](https://css-tricks.com/a-complete-guide-to-dark-mode-on-the-web/)