5

I want a div to have the "grayed out" look that you get with filter:opacity(0.5) on a white background, but I don't want to actually make it see-through. How do I do this? I know one solution would just be to put another element with a white background behind it, but I'm looking for a simpler solution with just CSS filter effects.

0

2 Answers 2

5

I am going to explain the math behind the answer of @codebreaker and try to provide a generic formula. I will rely on a previous answer where I defined the formula of some filter functions.

We have f(x) = p*(x - 255/2) + 255/2 for contrast(p) and f(x) = x*p for brightness(p)

To obtain contrast(p1) brightness(p2) we mix both functions

f(x) = p2*(p1*(x - 255/2) + 255/2)

We simplify to

f(x) = p1*p2(*x - 127.5) + p2*127.5

Now for opacity(p) we have ref

f(x) =  x*p + C*(1 - p)

Where C is the background color of the image and in your case you considered white so we can replace it by 255 to get

f(x) =  x*p + 255*(1 - p) = p*(x - 255) + 255

Now we simply want both function (the opacity one and the contrast-brightness one) to be the same thus we can write

p1*p2*(x - 127.5) + p2*127.5 = p*(x - 255) + 255

For p=0.5, p1=1/3, p2=1.5=3/2 we will have

1/2*(x - 127.5) + 3/2*127.5 = 1/2*(x - 255) + 255
1/2*x - 127.5/2 + 3/2*127.5 = 1/2*x + 127.5
...
0 = 0

This explain the relation between the values you are using. You can also find any other combination of values you want.

For a generic case we can consider:

p1*p2*(x - 127.5) + p2*127.5 = p*(x - 255) + 255
x*(p1*p2 - p) + p2*127.5*(1 - p1) + p*255 - 255 = 0

We should have (p1*p2 - p) = 0 and p2*127.5*(1 - p1) + p*255 - 255 = 0 so we get

p1*p2 = p
p2*(1 - p1) = (1 - p)*2

and finally

p2 = 2 - p
p1 = p/(2 - p)

If we consider an opacity of 0.25 we will have:

p2 = 1.75
p1 = 1/7

img:first-of-type {
  filter: opacity(var(--o,1));
}

img:last-of-type {
  filter: contrast(calc(var(--o,1)/(2 - var(--o,1)))) brightness(calc(2 - var(--o,1)));
}
<div>
  <img  height="126" width="168" src="https://upload.wikimedia.org/wikipedia/commons/6/66/SMPTE_Color_Bars.svg">
  <img  height="126" width="168" src="https://upload.wikimedia.org/wikipedia/commons/6/66/SMPTE_Color_Bars.svg">
</div>

<div style="--o:0.75">
  <img  height="126" width="168" src="https://upload.wikimedia.org/wikipedia/commons/6/66/SMPTE_Color_Bars.svg">
  <img  height="126" width="168" src="https://upload.wikimedia.org/wikipedia/commons/6/66/SMPTE_Color_Bars.svg">
</div>

<div style="--o:0.5">
  <img  height="126" width="168" src="https://upload.wikimedia.org/wikipedia/commons/6/66/SMPTE_Color_Bars.svg">
  <img  height="126" width="168" src="https://upload.wikimedia.org/wikipedia/commons/6/66/SMPTE_Color_Bars.svg">
</div>

<div style="--o:0.25">
  <img  height="126" width="168" src="https://upload.wikimedia.org/wikipedia/commons/6/66/SMPTE_Color_Bars.svg">
  <img  height="126" width="168" src="https://upload.wikimedia.org/wikipedia/commons/6/66/SMPTE_Color_Bars.svg">
</div>

1
  • 1
    Wow, this is great, thanks! I was hoping someone who understood the math behind this could give me a generic formula. Commented Oct 3, 2019 at 23:44
4

Setting the contrast to 1/3 and the brightness to 1.5 simulates 0.5 opacity. In the demo, the second test pattern has opacity 0.5, but when you mouse over it, it changes to the alternative filter, and you can see from the non-overlapping area that the colors are exactly the same, at least as far as human eyesight can tell.

I'm not exactly sure what the relationship is here. If you want to simulate 0.25 opacity, then you might expect that 1/6 contrast and 1.75 brightness would work, and it's close, but there are faint changes. It's not as exact as it is for 0.5 opacity.

img {
  position: absolute;
}

#img1 {
  top: 0px;
  left: 0px;
}

#img2 {
  top: 40px;
  left: 84px;
  filter: opacity(0.5);
  
}


#img2:hover {
  filter: contrast(calc(1/3)) brightness(1.5);
}
<img id="img1" height="126" width="168" src="https://upload.wikimedia.org/wikipedia/commons/6/66/SMPTE_Color_Bars.svg">
<img id="img2" height="126" width="168" src="https://upload.wikimedia.org/wikipedia/commons/6/66/SMPTE_Color_Bars.svg">

1
  • for you second case, it should be 1/7 instead of 1/6. I updated my answer to explain it as well Commented Oct 3, 2019 at 23:18

Not the answer you're looking for? Browse other questions tagged or ask your own question.