17

How can I set mix-blend-mode on an element, but not it's children? Setting the children to the default value of normal does not seem to work:

http://jsfiddle.net/uoq916Ln/1/

Choylton B. Higginbottom
  • 2,236
  • 3
  • 24
  • 34
  • It is the same issue as with opacity. It is not really that the property is inherited, it is that all the block is rendered with this effect – vals Jul 25 '15 at 20:36

5 Answers5

15

The solution on how to avoid mix-blend-mode affects children:

  1. Make child element position relative, give it a width and height;
  2. Create some real or pseudo element inside the child with absolute position, and apply mix-blend-mode to it;
  3. Create inner element inside the child for your content. Make it's position absolute, and put it on top of other elements;

Live example

html

<div class="bkdg">
    <div class="blend">
        <div class="inner">
            <h1>Header</h1>
        </div>
    </div>
</div>

css

.blend {
    position: relative; /* Make position relative */
    width: 100%;
    height: 100%;
}

.blend::before { /* Apply blend mode to this pseudo element */
    content: '';
    width: 100%;
    height: 100%;
    position: absolute;
    left: 0;
    top: 0;
    z-index: 1;
    background-color: green;
    mix-blend-mode: multiply;
}

.inner { /* This is our content, must have absolute position */
    position: absolute;
    z-index: 2;
}

h1 {
    color: white;
}
Theblockbuster1
  • 308
  • 6
  • 15
Rashad Ibrahimov
  • 3,279
  • 2
  • 18
  • 39
6

I know this was asked over two years ago, but it could be useful in the future as it could be a better solution than creating pseudo-elements.

There is the CSS isolation property that allows to choose wether the child element should be rendered in its parent's context (auto) or as part of a new context, thus without any blend mode applied to it (isolate).

Check out this page for examples

mrcendre
  • 1,053
  • 2
  • 15
  • 28
  • 2
    If I'm right isolation only guards siblings of an element against mix-blend-mode. It does not work on children. – Driesketeer Mar 10 '20 at 12:54
4

someone commented that the the whole block is rendered with the effect and that is why you're having the issue. I am able to accomplish what you're are trying to do by removing the h1 from the block, position absolute, and a z-index of 1. here is a jsfiddle to show the effect.

html

<div class="bkdg">
    <h1>Header</h1>
    <div class="blend">
    </div>
</div>

css

.blend {
    background-color: green;
    mix-blend-mode: multiply;
    width: 700px;
    height: 35px;
}
h1 {
    color: white;
    position: absolute;
    top: -15px; left: 10px;
    z-index: 1;
}

https://jsfiddle.net/jckot1pu/

Jpsh
  • 1,697
  • 12
  • 17
  • 2
    Well that is a tough question. While it accomplishes what you want you may not consider this a solution and I can understand that. However in my experience I have found sometimes to accomplish the css styling you want sometimes you have to do things in a round about way. – Jpsh Jul 26 '15 at 16:25
3

It’s impossible to remove an element’s mix-blend-mode from its children.

MDN says that mix-blend-mode:

sets how an element's content should blend with the content of the element's parent and the element's background

To achieve the desired effect, place the child in a separate stacking context and make sure it renders on top of the element with mix-blend-mode set.

You need two things to make this work:

  • Make sure that your opaque content (your text) is not a child of the element that sets the background and the blend mode. For example, with CSS Grid Layout.
  • Make sure the text is rendered over, and thus not affected by, the element that sets the background and the blend mode. Setting mix-blend-mode on your background will create a stacking context for it, and you may need to give your content its own stacking context to ensure it gets rendered above it.

Position your elements with CSS Grid:

  • define a grid container with one auto-sized grid area
  • place both the background element and the text element into that one grid area (so that they overlap)
  • let the text element dictate the size of the grid area
  • have the background element stretch to the size of the grid area, which is dictated by the size of the text element

Then, set isolation: isolate on the text element to ensure it gets rendered above, and not under the background element.

A working example

.container {
  display: grid;
  grid-template-areas: 'item';
  place-content: end stretch;
  height: 200px;
  width: 400px;
  background-image: url(https://picsum.photos/id/237/400/200);
  background-size: cover;
  background-repeat: no-repeat;
}

.container::before {
  content: '';
  grid-area: item;
  background-color: seagreen;
  mix-blend-mode: multiply;
}

.item {
  grid-area: item;
  isolation: isolate;
  color: white;
}

h1,
p {
  margin: 0;
  padding: 10px;
}
<div class="container">
  <div class="item">
    <h1>HEADLINE</h1>
    <p>Subhead</p>
  </div>
</div>
Andy Jakubowski
  • 474
  • 2
  • 4
0

An important note if you're using the excellent pseudoelement ::before/::after solution posted by Rashad Ibrahimov.

I found that I had to remove z-index from the parent element and apply it only to the pseudoelements and child elements before mix-blend-mode: multiply would work.

For example

#wrapper {
    position: relative;
}

#wrapper .hoverlabel {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    /* z-index: 90; Uncomment this to break mix-blend-mode. Tested in Firefox 75 and Chrome 81. */
}

#wrapper .hoverlabel::before {
    position: absolute;
    content: "";
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    mix-blend-mode: multiply;
    z-index: 90;
    background-color: rgba(147, 213, 0, 0.95);
}