XOR effect in CSS

Piotr Pliszko

XOR, or exclusive or is a logical operator that returns true only if exactly one of the two inputs is true. Mathematically speaking:

p \oplus q = (p∧¬q)∨(¬p∧q)

Let's implement a CSS effect based on this. In the end, we should achieve something like this:

screenshot
Source code

If you want to jump straight to the code, the source code for this post is available on GitHub: CSS Playground - XOR demo.

Preparation

First, let's prepare some base structure we will use to build the effect. Let's start with simple HTML

<div className="container">
  <div className="box"></div>
  <div className="text">XOR</div>
</div>

Elements .box and .text will be the ones to which we will apply XOR effect. Now, let's create simple styles for them.

.container {
  width: 1200px;
  height: 800px;
  border: 1px dashed #aeaeb3;
  display: flex;
  align-items: center;
  justify-content: center;
}

.box {
  width: 300px;
  height: 200px;
  background-color: black;
}

.text {
  color: black;
  font-size: 96px;
  font-weight: bold;
}

We should achieve something like this:

screenshot

Let's move the text a little bit to the left, to overlap with our box. This will allow us to later demonstrate XOR effect. Add transform to the text:

.text {
  /* ... */
  transform: translate(-90px);
}

We should now see that the text and the box overlap.

screenshot

Now, when we have our base prepared, let's blend!

Blend modes

In computer graphics, blend modes are used to determine how two layers are blended. The default behavior is that the higher layer covers whatever is present on the lower layer. There are however different blend modes that can be used. For a nice overview of these modes, take a look at the Blend modes Wikipedia page.

Today, we are especially interested in the "difference" blend mode. The difference blend mode takes the difference value of each pixel, inverting light colors. It subtracts either the blend color from the base color or the base color from the blend color, depending on which has the greater brightness value. If the color values are identical, they become black.

Fortunately for us, CSS provides us with an implementation of blend modes in the form of mix-blend-mode property. As MDN states:

The mix-blend-mode CSS property sets how an element's content should blend with the content of the element's parent and the element's background.

So, in our case, we will use mix-blend-mode: difference property to achieve our result. Let's add it to our .text styles.

.text {
  /* ... */
  mix-blend-mode: difference;
}

As we can see, nothing really changed. Why? As stated previously

If the color values are identical, they become black

So in our case, two black values for both .box background-color and .text color result in the same, black value. Let's now change them both to white to see the result.

.box {
  /* ... */
  background-color: white;
}

.text {
  color: white;
  /* ... */
}

Now we can definitely see some result!

screenshot

Again, the result is black, as we mix two identical colors. What will happen in case we select a different color? Let's try it out. I've decided to mix colors #374151 and #ea580c.

screenshot

Now we can definitely see this blend mode in action! But, for our demo, let's go back to white as a value of both attributes to achieve the final result.

Inverting

We now have our XOR effect in action, but it's not so impressive, as we cannot really see the box.

screenshot

What can we do to achieve the result we want? Let's use another really useful CSS feature called filter. As MDN states:

The filter CSS property applies graphical effects like blur or color shift to an element.

In our case, we want to invert the color to achieve white text over the black box. And this is exactly what we are going to use - the invert() function.

The invert() CSS function inverts the color samples in the input image.

So, let's add it to our .container styles, to invert colors inside of it:

.container {
  /* ... */
  filter: invert(100%);
}

Now, we should see our desired effect.

screenshot

The complete code looks like this:

.container {
  width: 1200px;
  height: 800px;
  border: 1px dashed #aeaeb3;
  display: flex;
  align-items: center;
  justify-content: center;
  filter: invert(100%);
}

.box {
  width: 300px;
  height: 200px;
  background-color: white;
}

.text {
  color: white;
  font-size: 96px;
  font-weight: bold;
  transform: translate(-90px);
  mix-blend-mode: difference;
}
Demo styles

Most of the properties above are just for demo purposes to show the result of our experiment in a nice way. You may want to create some different styles in your case, and keep only important parts that we talked about.

Source code

You can find the source code for this demo on my GitHub.

Resources

If you have any questions or feedback, feel free to reach out to me on GitHub, Twitter/X or LinkedIn!