---
title: Implement Dark Mode with One Line of CSS
subtitle: 'with `filter: invert(1)`, which is not ideal, but it can be a starting point'
date: '2023-09-25'
slug: dark-mode
---

Personally I do not like the dark mode. I love sharp full-black text on
clean white background, which gives me the same feeling as reading on
paper. Reading in the dark mode makes me feel lost in the dark, but I
know many people are the opposite, therefore in this crowded world of
numerous dichotomous choices, web developers and authors have one more
decision to make: should the website support the dark mode?

To support the dark mode well from the scratch is not a trivial task.
You may have to tweak a lot of colors, depending on how many colors you
use on your webiste. That is one of the reasons why I have almost
stopped using colors other than black, white, and gray after some years
of wrestling with colors. Eventually I felt my decision fatigue
outweighed the website appearance. One exception for now is syntax
highlighting for code blocks, which I might also give up someday.

Of course, you can use a CSS framework, and there exist many, such as
Bootstrap. They are extremely flexible, which means you can make [a
great many
decisions](https://getbootstrap.com/docs/5.3/customize/overview/) if you
would like to, e.g., what color `--bs-info-border-subtle` or
`$purple-600` should really be. Bear in mind that we use a tool to save
our time. I, for one, often get hooked on customizing things if I'm
offered the chances.

## Invert all colors

To get started with the dark mode from the light mode can be simple and
quick, though. You can invert the colors of everything with a single CSS
filter:

``` css
html {
  filter: invert(1);
}
```

That is, black will become white, and white will become black.
Generally, for a color `rgb(x, y, z)`, the fully inverted color will be
`rgb(255 - x, 255 - y, 255 - z)`, where `x`, `y`, and `z` range from `0`
to `255`.

Another simple starting point can be
`color: white; background-color: black;`, but it only works for text and
not other elements such as borders.

## Set an opaque background color first

There is one caveat: if the background color of the page is transparent
(which is the default), it will not be inverted, because transparent
white and transparent black are identical. The page will not look good.
In this case, you have to set an opaque light background in the light
mode, e.g.,

``` css
html {
  filter: invert(1);
}
body {
  background: white;
}
```

You can try this on any web page in the "Styles" pane of the Developer
Tools of your web browser, and you are likely to find the page look
funny, especially the images. Yes, even colors on images will be
inverted, which will make an image look like the film of a photograph,
e.g.,

<style type="text/css">
.main img { filter: invert(1); }
</style>

![An inverted logo](/images/logo.png)

To fix that problem, you can invert visual elements like images one more
time to restore their colors:

``` css
html, img, video, iframe {
  filter: invert(1);
}
```

If your page only uses black, white, and gray, this "dark mode" should
be good enough. If not, it is simple enough to tweak.

## All colors are opposite now

With this `invert(1)` filter applied to the top-level `<html>` tag, you
need to think colors in the opposite way for all elements. For example,
if you do not want the body background to be fully black, you need to
set it to lightgray, e.g.,

``` css
body {
  background-color: rgb(230, 230, 230);  /* or #e6e6e6 */
}
```

That will be inverted to darkgray.

## Automatic dark mode

If you want automatic (adaptive) dark mode when the system uses the dark
mode, you can use the media query:

``` css
@media (prefers-color-scheme: dark) {
  html, img, video, iframe {
    filter: invert(1);
  }
  body {
    background-color: white;
  }
}
```

Do you want to give readers a choice to manually specify whether they
want the dark mode or not? That is yet another decision to make. In the
end, we lament that "Time flies!" But time never flies. Desire does.
