Is it possible to achieve this with mix-blend-mode?

Viewed 181

I'm trying to achieve something with mix-blend-mode and I'm wondering if it's possible. I want to use mix-blend-mode to create a "multiply" effect, while keeping the text within it a solid white. I have seen similar issues discussed here, but slightly different than my situation, I believe...

My (simplified) HTML:

    <div class="box">
            
            <div class="content">
                
                <div class="container">
                    <h1 class="header">HEADLINE</h1>
                    <div class="description"><p>Subhead</p></div>
      </div>
      
        </div> <!-- .content -->
            
    </div>

...and my CSS:

.box {
  display: flex;
align-items: flex-end;
    height: 300px;
width: 700px;
    background-image: url(https://heroshockey.com/wp2021/wp-content/uploads/2021/07/program-billboards-future-stars.jpg);
background-size: cover;
background-repeat: no-repeat;
    }
    
    
.content {
  float: left;
  width: 100%;
}

.container {
background-color: red;
 mix-blend-mode: multiply;
}

h1, p {
  color: white;
  margin: 0;
  padding: 10px;
}

Here's a fiddle of the result:

https://jsfiddle.net/cosmocanuck/7zhwgo0s/55/

But I need the text to remain white, not "cut out".

I'm trying to follow Rashad's response here:

Remove mix-blend-mode from child element

But my situation, while very close, is somewhat different (including using flexbox to bottom-align the element containing the text), and so far I've failed to crack this one despite many attempts.

Can anyone point me in the right direction? Thanks!

1 Answers

Make sure the blended background and the text are in separate stacking contexts, and that the text is rendered over the background, not under it.

The reason your current code doesn’t work is that the text element is a child of the container element that sets the mix-blend-mode. mix-blend-mode will blend all of the content of the container, including the text — there’s no escaping that.

You need two things to make this work:

  1. Make sure that the text is not a child of the element that sets the background and the blend mode.
  2. Make sure the text is rendered over, and thus not affected by, the element that sets the background and the blend mode.

The challenge is that the size of the background must be dependent on the size of the content. One way to do this is with CSS Grid Layout:

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

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

.container {
  display: grid;
  grid-template-areas: 'item';
  place-content: end stretch;
  height: 300px;
  width: 700px;
  background-image: url(https://heroshockey.com/wp2021/wp-content/uploads/2021/07/program-billboards-future-stars.jpg);
  background-size: cover;
  background-repeat: no-repeat;
}

.container::before {
  content: '';
  grid-area: item;
  background-color: red;
  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>

Related