Night Mode 🌚🌝 with Mix Blend Mode: Difference
Tags: css
Published: 2019-05-04 16:28:15

I came across the idea of _implementing night mode using_ `mix-blend-mode: difference` when I was preparing for my talk [This World Mixed and Blended](https://speak.wgao19.cc/slides/1904-mixed-and-blended) (these slides require color font support, i.e., FireFox / Safari). The original idea was only meant for a showcase and [the browser support is currently limited](https://caniuse.com/#search=mix-blend-mode).

Still, I implemented it to my dev blog and had a lot of fun. So this will be what this post is about. Meanwhile, as I thought more about it, it occurs to me now that this _can be_ a legit preferred way night mode is to be done. And let me share a bit about the intuition behind that as well.

<!-- excerpt ends -->

Please note that this is not a standard way of implementing dark mode. To see how people normally do it, you may want to see [here](https://github.com/gaearon/overreacted.io/blob/master/src/html.js#L47) for how Dan Abramov does it for Overreacted, or [this post](https://dev.to/ananyaneogi/create-a-dark-light-mode-switch-with-css-variables-34l8).

![](https://i.imgur.com/vzexItp.gif)

## Understanding `mix-blend-mode: difference`

`mix-blend-mode: difference` is one of the blending modes CSS supports, as specified in [Compositing and Blending Level 1](https://www.w3.org/TR/compositing-1/). [Blending modes](https://www.w3.org/TR/compositing-1/#blending), in turn, specify how colors mix when graphics are stacked together. The graphics do not limit to images. Any rendered graphics, content of divs, texts, emojis 🌚🌝, svgs, may participate in the blending.

I'd like to think of that as the process where browsers check with us whenever they are about to paint a new element: “Hey, it seems that this next pixel I'm painting is red, but currently I am seeing a blue on the ground, by any chance you want me to combine those colors together, and if so, how?”

Blending modes allow us to create some very interesting effects such as overlaying texts on an image while revealing the texture of the graphics directly in a browser:

![](https://www.w3.org/TR/compositing-1/examples/overlay_text.png)

Example of blending mode from [Compositing and Blending Level 1](https://www.w3.org/TR/compositing-1/)

### Definition

`mix-blend-mode: difference`, in particular, is one of the blending modes that is defined as taking the absolute value of the difference between the two colors:

```
difference(A, B) = |A - B|
```

This is an abstraction of the actual computation, where for each color we meant three numbers representing the R, G, and B channels, respectively. As an example,

```
difference(
    rgb(255, 255, 255),
    rgb(0, 100, 200)
) = rgb(255, 155, 55)
```

Meanwhile, taking the absolute value makes the operation _commutative_, meaning `B(A, B) = B(B, A)`. In another words, it doesn't matter which graphic is laid on top of the other, the result is going to be the same.

### Intuition

What exactly is the blending mode _difference_ doing? The definition seems straightforward. However, our minds may not be very good at perceiving what will happen to the resultant color before actually seeing them. To name an example, _difference(white, red)_ is essentially deducing the complement color of red, which is teal. But how can you “see” it until you actually _saw_ it?

The following pen is created by blending intersecting stripes of whites and reds only.

{%codepen https://codepen.io/wgao19/pen/GaKXZr %}

Perhaps exactly because its hardly perceivable effects, we can use `mix-blend-mode: difference` to create some contrasting visuals to spark surprises in UI, like this one by [Sven Wolfermann](https://codepen.io/maddesigns/):

{%codepen https://codepen.io/maddesigns/pen/dpGVGw %}

This was the design that inspired me to think about creating dark mode using _difference_.

## Implementing dark mode using `mix-blend-mode: difference`

### How does it work?

The idea is simple: When night mode is turned on, cover your site with a full-screen div with `background: white` and `mix-blend-mode: difference`, and save yourself the trouble of defining all the colors again 😉

```css
.dark-mode-screen {
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
  background: white;
  mix-blend-mode: difference;
}
```

Why, though?

The thought process involves two of the mathematicians' favorite numbers, 0 and 1.

In unit space, 0 means _nothing_ and 1 means _everything_.

In RGB colors, 0 means black and 1 (100% or 255) means white.

Now let's think about our blog site, and let's assume that currently we have a white background and a black foreground. If we take the difference of that page against white, what do we get?

- background: currently white, the difference between white and white is _nothing_, which means _black_
- foreground: currently black, which means _nothing_, the difference between white and nothing is _white_

Now we have a _black background_ and a _white foreground_, and look what is that? A reversed (dark) theme :)

Furthermore, when taking the _difference_ of the page with _white_, the contrast between the original background and foreground is automatically preserved. This does not only apply to blacks and whites. In fact, my code block with original theme _Dracula_ picks up a nice sepia-styled hue when inverted.

![](https://i.imgur.com/i2lauxj.png)

![](https://i.imgur.com/4OY7tMn.png)

### Transition

**Scaling a div**

I put a toggle fixed to the lower bottom of the page. When the night mode is toggled on, I transform that toggle to scale up that covers the whole page. This effect is kind of showing off the original UX design.

![](https://i.imgur.com/vzexItp.gif)

**Opacity**

If you prefer a more subtle toggle, maybe tweaking `opacity` is a better choice and the blending mode will work just as nice. With an easing timing function on the transition, this is perhaps the transition UI shared by most websites and blogs implementing a night mode.

![](https://i.imgur.com/CsEehnx.gif)

### Isolate the elements that are not intended to blend

As mentioned earlier, `mix-blend-mode` will apply on _any_ graphics. However, when implementing dark mode, some elements may not be intended to blend. Such examples include logos of specific colors, emojis, etc. To specify that, you can put `isolation: isolate` ([reference](https://www.w3.org/TR/compositing-1/#isolation)) on those elements. Note that this will create a stacking context on the element that is isolated.

```css
/* twitter logo and emoji should not blend */
.twitter-logo,
.emoji {
  isolation: isolate; 
}

/* elements that create their a stacking context are automatically isolated */
.footer {
  z-index: 1;
}
```

### Pick a colored hue for your dark mode

As mentioned earlier, taking the difference between the page against white is like taking the complement color directly. You may not have to limit that choice of color to white. In fact, you can pick any color to take the difference with. For an effect of a darker theme where your site was initially on a light background, you may consider any color that is relatively light:

![](https://i.imgur.com/5exkPxU.gif)

These are some of the technical details I've encountered. If you're trying this and encounter issues here and there, please feel free to let me know and I'd be glad if I can help.

To see this thing in code, check out this CodePen:

{%codepen https://codepen.io/wgao19/pen/vMzOgQ %}

## Limits

(apart from being geeky 😅)

- You don't have full range of colors. It saves you the trouble of specifying more colors, but you'd lose control over the dark colors — they'd be deduced from your light colors. Yet this may not be a limit but rather a design choice.
- Browser support of `mix-blend-mode` is still not optimal, [you're stuck with non-IE, relatively newer versions of browsers](https://caniuse.com/#search=mix-blend-mode).
- Performance can be a concern as well for its by-pixel and by-channel computation. Don't do crazy animations with it (yet).

## Till next time 🤞

Blending modes were citizens from computer graphics. I remember first playing around with them in Photoshop years ago. Now that browsers are becoming more powerful and we are seeing complex graphical features native to browser rendering. This doesn't mean we should implement a full photoshop in browsers and nor should we limit our imaginations to just that. Browsers and web pages have their own contexts and goals, as well as a different set of limits. Maybe we should welcome them like new habitants and discover use cases native to this territory.

Once again, the inspiration credits to [this CodePen](https://codepen.io/maddesigns/pen/dpGVGw), a cute animation that strikes an impression at first sight. You should _hover over it_ 🙈

{% codepen https://codepen.io/maddesigns/pen/dpGVGw %}

I hope this serves as a friendly discovery about blending modes, and I hope you had fun same as I do.