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
istransparent
(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
iswhite
(set in the CSS. The value cannot be stolen, thanks to thehtml
"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.
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
.
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.
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.
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!
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.
Top comments (0)