The trouble with Chrome, iframes and mix-blend-mode
Tags: html, css, webdev
Published: 2020-05-26 08:30:55

This is a "bonus" for my post [What is the color of a blank page?](https://dev.to/bcalou/what-is-the-color-of-a-blank-page-49og)

In this article I will describe a very specific bug happening in Chrome, and explore what it teaches us about the canvas behavior I explained in the original post.

If you can, I recommend that you use Chrome to read this article, so you can see what happens.

# What is the bug?

In Chrome, **inside an iframe, an element cannot blend with the iframe's canvas**.

To show this, CodePen is perfect: indeed, all you see inside a pen is running inside an iframe.

Let's reuse the example of the original post, where we want to mix the `h1` with a white background set on the `body`:

```css
body {
  background-color: white;
}

h1 {
  color: green;
  mix-blend-mode: difference;
}
```

This is valid CSS which should produce a pink title (pink being the difference between green and white).

{% codepen https://codepen.io/bcalou/pen/RwWdKXe %}

At the time of writing (may 2020) :
- ✔️ Firefox works fine (pink title)
- ✔️ Safari works fine (pink title)
- ❌ Chrome fails (green title) - I made it work in the original post with a little trick I will explain later

According to what we learned in the original post, here's what happens:
- The `body` is `transparent` (`white` set in the CSS, but "stolen" by the canvas)
- The `html` is transparent (default value of background-color)
- The canvas is white (the value is taken from the `body`)

In Firefox and Safari, the `h1` has no problem blending with the white canvas. But it doesn't work in Chrome.

As I said, this only happens because CodePen runs our code inside an iframe: [the exact same code works perfectly in isolation](https://css-experiments.netlify.app/chrome_debug.html).

# How do you fix it?

It's very simple. But it's what this fix reveals that interests me. Remember the thing about the canvas "stealing" the background color from the `body`, and the `body` not repainting it, so ending up transparent? From the [w3c](https://www.w3.org/TR/css-backgrounds-3/#root-background):

<blockquote>The background of the root element becomes the background of the canvas [...] The root element does not paint this background again, i.e., the used value of its background is transparent.</blockquote>

Well, Chrome has no problem blending an element with the background of an iframe's `body`. But the background being stolen by the canvas is what breaks it.

Can we prevent the canvas from committing such a mischievous act?

Yes, by setting a background-color to the `html` element as well.

```css
html, body {
  background-color: white;
}
```

{% codepen https://codepen.io/bcalou/pen/GRpeWra %}

It now works everywhere!

I did use this fix before, but only now I understand why it works: it's because it prevents the canvas from stealing the `body`'s background.

Remember our little algorithm?

```js
if (the html has a background-color) {
  use it to paint the canvas
} 
else if (the body has a background-color) {
  use it to paint the canvas
}
else {
  the canvas stays transparent
}
```

It's against the w3c guidelines, but if you set a background on the `html` element, it will be used by the canvas. The `body` will be left alone.

So here's what happens:

- The `body` is `white` (set in the CSS. The value cannot be stolen, thanks to the `html` "taking the bullet")
- The `html` is transparent (`white` set in the CSS, but stolen by the canvas)
- The canvas is white (the value is taken from the `html`)

We could say that we set up a lure with the `html`'s background, so that the `body` can keep its own background, and the title can blend.

<figure>
![](https://dev-to-uploads.s3.amazonaws.com/i/6fokdzvhas66s567djhx.gif)
<figcaption>body hiding from the canvas to keep its background</figcaption>
</figure>

A confusing thing is that any background-color will do. Even one that is virtually invisible:

```css
html {
  background-color: rgba(255, 255, 255, 0.01);
}

body {
  background-color: white;
}
```

The `rgba` background is almost invisible, but the result is the same.

{% codepen https://codepen.io/bcalou/pen/bGVZqaE %}

This was quite confusing to me. The value has no impact?

Well, it's easy to understand why now. The value does not matter at all. The only thing that matters is that *there is* a value, and it "distracts" the canvas, which won't steal the background from the `body`.

Let's now look at an even more specific case and how it can shed a new light on the relationship between the `body` and the canvas.

# Absolutely insane

The `html` background color fix is simple, but there is an interesting case which is not covered by this trick.

Let's add an absolutely positioned pseudo-element on our title.

```css
h1::after {
  content: 'Hello from the other side';
  position: absolute;
  bottom: 50px;
  right: 50px;
}
```

As you can see, it will land at the bottom right of the page, *outside* the `body` and the `html` elements. I don't think the gods of CSS ever intended this, but woops, I just did it.

What color do you think the pseudo-element will be?

{% codepen https://codepen.io/bcalou/pen/RwWdgpx %}

If you are using Chrome, you can see that the element is green. It illustrates the bug well: inside an iframe, an element can blend with the `body`, but not with the canvas, though both are white.

And now for the final touch: you can clearly see the distinction between the white `body` and the white canvas as you resize the viewport to bring the pseudo-element above the `body`.

![The element blends with the body, not with the canvas](https://dev-to-uploads.s3.amazonaws.com/i/61imdtfkpw1piu5knogq.gif)

Let's analyze it:
- The text blends with the `body` (`white` set in the CSS)
- The text does not blend with the `html` (`white` set in the CSS but stolen by the canvas, so transparent)
- The text does not blend with the canvas, though it's white, because of the Chrome iframe bug.

# Why do I care so much?

It might seem overkill to care about such extremes situations. That's not your daily CSS problem, obviously. Think about it: to reproduce what we can see in the video above by accident, you'd have to:

- use Chrome
- use an iframe (created by CodePen, for example)
- use `mix-blend-mode`
- use it on an element without any of its parents having a background color
- use an absolutely positioned element
- work with a body small enough that the absolutely positioned element lands outside of it

Well, believe it or not, that's exactly what happen to me some time ago.

I was helping a student of mine to create some `mix-blend-mode` effect. I innocently created a demo on CodePen, using Firefox. It worked well! Case closed. Until my student informed me that it didn't work in Chrome.

<figure>
![](https://dev-to-uploads.s3.amazonaws.com/i/4oz6nz4m1ly7m4n12fap.gif)
<figcaption>I'm not worthy to be your teacher anymore</figcaption>
</figure>

Well, it took me quite a while to understand all I explained in these two posts. To be honest, I had one of these "fix first, understand later" moment. I added a background-color to the `html`, forced the `body` to `100vh`, and *voilà*!

A few months later, I sat down and tried to analyze the problem… right down the rabbit hole.

# Making things right 💪

Despite the `html` background color being an easy fix and despite the absolutely positioned element situation being quite convoluted, it is still a bug.

Actually, it used to be a bug even outside of iframes! A [ticket was opened on the Chromium platform](https://bugs.chromium.org/p/chromium/issues/detail?id=711955) in 2017. It was marked as fixed in 2020.

Realizing that the bug still occurred in iframes, I allowed myself [to comment about it](https://bugs.chromium.org/p/chromium/issues/detail?id=711955#c16), which led to the creation of a dedicated issue.

![Achievment Unlocked](https://dev-to-uploads.s3.amazonaws.com/i/yisaid6qyxiin361cod9.png)

# Beware of your testing habits

I **love** CodePen. I'm sure I don't need to explain why. I use it almost every day.

But in this very specific case, it put me in a situation which was **not** the same as the environment I was actually targetting. I was helping a student to create a page without any iframe involved. By using a tool that ran my code inside an iframe, I changed the initial conditions. As we learned, it is quite a drastic change when dealing with Chrome and `mix-blend-mode`.

Note that CodePen is not to blame: Chrome is responsible here, and of course CodePen *has* to run my code inside an iframe — except if you're using the debug view!

<figure>
![](https://dev-to-uploads.s3.amazonaws.com/i/0kcclwtvop1izj37l0wb.jpg)
<figcaption>Use the CodePen debug mode to run your code inside a real page — no iframe involed!
</figure>

I am also to blame: I have such a blind trust towards CodePen that I unconsciously dismissed the hypothesis that testing inside it could affect the result.

So, as they say: don't fall in love with your tools. There's nothing like a real test.