DEV Community

loading...
Cover image for The trouble with Chrome, iframes and mix-blend-mode

The trouble with Chrome, iframes and mix-blend-mode

bcalou profile image Bastien Calou ・6 min read

This is a "bonus" for my post What is the color of a blank page?

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:

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).

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.

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:

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.

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.

html, body {
  background-color: white;
}

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?

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.

body hiding from the canvas to keep its background

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

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.

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.

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?

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

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.

I'm not worthy to be your teacher anymore

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 in 2017. It was marked as fixed in 2020.

Realizing that the bug still occurred in iframes, I allowed myself to comment about it, which led to the creation of a dedicated issue.

Achievment Unlocked

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!

Use the CodePen debug mode to run your code inside a real page — no iframe involed!

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.

Discussion (1)

pic
Editor guide