2

I have following CSS code:

div {
  background: url('image-url'), linear-gradient(gradient), url('image-url-2');
  background-blend-mode: blend-mode-1, blend-mode-2;
}

The position of gradients or url backgrounds can change. I think that should have have any effect on blending order. My question is how are these modes applied to calculate final value?

Do browsers first apply blend-mode-1 on url('image-url') and linear-gradient(gradient) and later apply blend-mode-2 on the result of first and url('image-url-2') or is it the other way around?

Am I using the correct number of background-blend-modes or do I need to specify 3 of them?

vals
  • 61,425
  • 11
  • 89
  • 138
SanJeet Singh
  • 1,291
  • 2
  • 15
  • 28

2 Answers2

1

The stacking order of the background-images is the key factor here.

Your background images are stacked in the reverse order, the first in the list being the uppermost in the rendering stack.

The blendmodes are applied as any comma separated property applied on backgrounds, the first one to the first image, the second one to the second, and so on.

in your example

div {
  background: url('image-url'), linear-gradient(gradient), url('image-url-2');
  background-blend-mode: blend-mode-1, blend-mode-2;
}

url2 is at the bottom.

Above it, you have the gradient, with blend-mode-2 applied.

And above it, the image-url with blend-mode-1.

You could set a third background-blend-mode. In this case, it would aply to the blend between image-url-2 and background-color (That you don't set in your example, but that could be set)

vals
  • 61,425
  • 11
  • 89
  • 138
1

Blending takes place from back to front.

In other words, each element (image or gradient) is blended with the result of blending all underlying elements.

This is important, because many blend modes are not associative. An example is difference. Suppose you have 3 elements:

  • A = solid color #fff (front element; closest to user)
  • B = solid color #666
  • C = solid color #666 (back element; farthest from user)

What will happen is this:

  • Blending B with C; the result is black (#666 - #666 = #000).
  • Blending A with the result from the previous step (black); the result is white (#fff - #000 = #fff).

If blending would have started from the front element, then the result would have been 'dark charcoal':
(#fff - #666) - #666 = #333.

Live demo:

.p1 {                   /* |#fff - |#666 - #666|| = #fff */
   background-image:
      linear-gradient(#fff, #fff),       /* front element */
      linear-gradient(#666, #666),
      linear-gradient(#666, #666);       /* back element */
   background-blend-mode: difference, difference, normal;
}

.p2 {                   /* |#666 - |#666 - #fff|| = #333 */
   background-image:
      linear-gradient(#666, #666),       /* front element */
      linear-gradient(#666, #666),
      linear-gradient(#fff, #fff);       /* back element */
   background-blend-mode: difference, difference, normal;
}
<p class="p1">White</p>
<p class="p2">Dark charcoal</p>

Here's what the specs had to say about it. From Compositing and Blending Level 2:

Conceptually, the colors in the source element are blended in place with the backdrop.

and:

The backdrop is the content behind the element and is what the element is composited with. This means that the backdrop is the result of compositing all previous elements.

Still a bit vague; composition and blending are related, but not the same. But the logical interpretation is: blending should work from back to front. I think all major browser vendors have followed that interpretation.

It makes sense to specify the third blend mode.

As already pointed out by vals, a third blend mode can be specified to blend the third element with the background-color. This blend step will always come first, since background color is always behind any of the elements specified in background-image. You could say background-color acts like a fourth element.

However, the absence of the third blend mode does not mean 'do not blend'! From Compositing and Blending Level 2:

If a property doesn’t have enough comma-separated values to match the number of layers, the UA must calculate its used value by repeating the list of values until there are enough.

In other words, background-blend-mode: <bm1>, <bm2> is automatically expanded into: background-blend-mode: <bm1>, <bm2>, <bm1>

I won't deny this third blend mode has no effect, but that is for an entirely different reason: the absence of background-color. From the same document:

Everything in CSS that creates a stacking context must be considered an ‘isolated’ group.

and:

In an isolated group, the initial backdrop shall be black and fully transparent.

and:

Cs = (1 - αb) x Cs + αb x B(Cb, Cs)

A fully transparent background (αb = 0) causes the result of blending to be discarded, and to keep the color of the source element (here: the third element). This is what happens when no background color is specified.

There is always the possibility that somebody will add a background color later. To avoid unexpected color changes, I would recommend to always specify an explicit blend mode for the element in the back. If you don't want to blend with the background color (if any), then just say so, by adding normal to the end of the list of blend modes. It may prevent nasty surprises in the future, especially when <bm1> (the front blend mode, which will automatically occupy the empty spot in the back) is kind of an exotic blend.

Ruud Helderman
  • 10,563
  • 1
  • 26
  • 45