67

I'm coding a "popup window" in JavaScript and I've come across an interesting thing:

Cropped screenshot demonstrating strange stacking behavior

The navy square under the popup window is visible even though I would expect it to be hidden. The popup was added after the square, so it should be on the top.

CSS opacity property of the navy square is 0.3. From what I've tried, it seems that every number from the interval (0,1) would yield the same result. If I change it to 1, then it behaves as expected (i.e. the part of the square under the popup is hidden).

I've tried to set the z-index property to 10 for the square and 100 for the popup, but it doesn't change anything.

What am I missing? Why is part of square displayed?

Tested browsers:

  • Firefox 3.6.x
  • Chrome 4
0b10011
  • 18,397
  • 4
  • 65
  • 86
MartyIX
  • 27,828
  • 29
  • 136
  • 207
  • I'd guess it may be a bug/not implemented feature. However as I don't know much of CSS I don't think I qualify for answering it. Does other browser also display it in the same way? What is the browser you're using? – Maciej Piechotka May 16 '10 at 09:51
  • Do all elements of the popup window (Version, PosX, PosY...) have `z-index: 100;` or `z-index: inherited;`? – Cipi May 16 '10 at 09:51
  • 2
    i had the same problem once. i think this is a bug in $browser (firefox?) – knittl May 16 '10 at 10:25
  • My general opinion is designers should not be using opacity at all unless absolutely necessary. I think there are some ignorant designers out there using "opacity" as a way to choose the color they want (maybe lighter or more muted) not realizing the opacity parameter has weird side-effects, and that you can choose any color using only RGB values, without opacity. – PJ Brunet Oct 23 '18 at 02:36

8 Answers8

72

This is not a bug and is actually how it's supposed to work. It's a bit confusing as the elaborate description of Stacking Contexts doesn't mention anything about it. However, the visual formatting module links to the color module where this particular gotcha can be found (emphasis mine):

Since an element with opacity less than 1 is composited from a single offscreen image, content outside of it cannot be layered in z-order between pieces of content inside of it. For the same reason, implementations must create a new stacking context for any element with opacity less than 1. If an element with opacity less than 1 is not positioned, implementations must paint the layer it creates, within its parent stacking context, at the same stacking order that would be used if it were a positioned element with ‘z-index: 0’ and ‘opacity: 1’. If an element with opacity less than 1 is positioned, the ‘z-index’ property applies as described in [CSS21], except that ‘auto’ is treated as ‘0’ since a new stacking context is always created. See section 9.9 and Appendix E of [CSS21] for more information on stacking contexts. The rules in this paragraph do not apply to SVG elements, since SVG has its own rendering model ([SVG11], Chapter 3).

0b10011
  • 18,397
  • 4
  • 65
  • 86
  • 18
    Even if it is "How it's supposed to work", it is very counter-intuitive for more people. I would say this is a flaw in the spec. Here's an example to play with http://jsfiddle.net/WzvLU/1/ – Luke Aug 16 '13 at 18:11
  • 5
    @atilkan I believe this strange behavior was due to under-specified behavior and matching how browser's implemented it ([2003 spec](https://www.w3.org/TR/2003/CR-css3-color-20030514/) vs [2008 spec where this was specified](https://www.w3.org/TR/2008/WD-css3-color-20080721/)). As for `align-items`, etc, they're named that way because they aren't necessarily locked to an axis. That is, the property can remain the same but apply to a different axis if another property changes. (Tab/fantasai did some great work [making the names standard across specs](https://www.w3.org/TR/css-align-3/).) – 0b10011 Jun 27 '17 at 19:42
  • @atilkan Can you explain simply Maths? Not sure. But there isn't anything more accurate and right than this field. Careful with your twisted logic. Maybe you fail understanding the reason. – El pupi Apr 01 '23 at 21:54
23

It's not a problem of opacity being more important than z-index, rather than z-index being relative to their stacking context (see z-index in the CSS2 specification).

In other words, z-index are only significant within the context of a positioned ancestor (whether its relative, absolute or fixed). What you need to do to fix your problem is add a position: relative; to the element that contain both your popup and your navy square, and probably add it a z-index: 1; . Seeing your screenshot it will probably be a top element such as a wrapper div.

Guillaume Esquevin
  • 2,992
  • 20
  • 25
  • 5
    Actually, it *is* an issue related to `opacity`. See http://www.w3.org/TR/css3-color/#transparency -- Specifically "...implementations must create a new stacking context for any element with opacity less than 1. ..." – 0b10011 Jul 31 '12 at 14:17
  • the `position: relative` fix worked for me! thanks Guillaume Esquevin – Matías Cánepa Nov 18 '13 at 15:38
8

Workaround for two elements, like divs: add a 0.99 opacity to your top element, and the order of both is reestablished.

opacity: 0.99;
ЯegDwight
  • 24,821
  • 10
  • 45
  • 52
Enzo DB
  • 89
  • 1
  • 1
6

An alternative to using opacity, is to use a transparent colour (with an alpha value)

So, rather than using

{
    background: gray;
    opacity: 0.5;
}

You could try

{
    background: rgba(128,128,128,0.5);
}

It isn't identical, but I was encountering the same issue you were having, and the above fixed it.

Rich S
  • 3,248
  • 3
  • 28
  • 49
1

Example code might be needed to debug this problem.

You might put overflow: hidden and possibly position: relative in a DIV which surrounds all the editor objects to try to force the elements to only be drawn within that DIV, e.g:

<div style="overflow: hidden; position: relative">
    (Editor object buttons go here)
</div>

As a last resort, you could also try a iframe in between the two elements to try to stop them seeping through.

cryo
  • 14,219
  • 4
  • 32
  • 35
0

You might try to set the popup window's DIV like this using !important so the style doesn't change on applying new style or class:

background-color: white !important;
z-index: 100 !important;
opacity: 1.0 !important;

Then, make new CSS class:

.PopupElement
{
 z-index: inherited;
 opacity: inherited;
}

And add class to all elements in the window, like this for example:

<input value="posx" class="some_class PopupElement"/>

My guess is that this would work, since there is no priority in applying CSS attributes... as far as I know. =)

Cipi
  • 11,055
  • 9
  • 47
  • 60
0

I had the same issue. Using rgba instead of color/opacity solved my problem. Working with LESS (in the Bootstrap framework), the fade() function did the conversion for me.

  • 1
    The best advise, I think! For me that works: background-color: rgba(20,20,20, 0.7); This also works fine: background-image: linear-gradient(to bottom, rgba(0,0,0,0.8) , rgba(50,50,50,0.5)); – rick-rick-rick Oct 13 '19 at 19:17
0

Although @Guillaume Esquevin already gave a great answer, I will try to expand on it in case someone ignores what a stacking context is (like I did).

As you can read here, there is something called stacking context, which refers to a group of elements sharing a parent that move together in the stack. An example could be a div and all its children.

There are three ways to create a stacking context: in the root of the document (the html element), by positioning the parent element, and by changing the opacity of the parent to something lower than 1.

Then, if you have a div with opacity lower than 1 and you want some sibling element of this div to appear behind it (and its children), you can create a new stacking context on such sibling by setting its position to relative or by changing its opacity as well.

Mr. Duhart
  • 927
  • 12
  • 11