Reputation: 394
I am trying to understand why applying an opacity other than 1 to an element's child makes it always stack on top of the element's siblings.
.element {
width: 500px;
height: 100px;
background: orangered;
}
.child {
height: 150px;
width: 100px;
background: grey;
opacity: 0.5;
z-index: 1;
}
.sibling {
width: 400px;
height: 100px;
background: skyblue;
z-index: 2;
}
<div class="element">
<div class="child"></div>
</div>
<div class="sibling"></div>
Removing the opacity property (or setting it to 1) on the .child
makes it stack behind the .sibling
as expected, but for some reason an opacity value other than that changes its stacking order. It makes me think that .element
is somehow forming a stacking context, but as far as I can tell it doesn't meet any of the criteria on MDN's website.
The CSS standard doesn't have anything to say about this from what I understand:
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, then it is painted on the same layer, within its parent stacking context, as positioned elements with stack level 0. If an element with opacity less than 1 is positioned, the ‘z-index’ property applies as described in [CSS21], except that if the used value is ‘auto’ then the element behaves exactly as if it were ‘0’. 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).
I think all its saying is that if you set opacity to something other than 1, then the element will form its own stacking context and everything inside the transparent element will be transparent together. But in my example, .child
may create its own stacking context for elements laid out inside of it, but the element itself would still be in whatever the surrounding stacking context is. This can be tested by setting display: flow-root
on .child
and removing opacity: 0.5
to force it to create a stacking context, but it will stack behind .sibling
as I would normally expect.
So what's going on here?
Upvotes: 4
Views: 361
Reputation: 273389
To start with, the z-index
you are applying are useless since no element is positonned. Your code can be simplified like below:
.element {
width: 500px;
height: 100px;
background: orangered;
}
.child {
height: 150px;
width: 100px;
background: grey;
opacity: 0.5;
}
.sibling {
width: 400px;
height: 100px;
background: skyblue;
}
<div class="element">
<div class="child"></div>
</div>
<div class="sibling"></div>
Then it's not about stacking context but painting order. What you said about stacking context is correct but is irrelevant here since you have no element inside .child
.
From your quote you will read:
If an element with opacity less than 1 is not positioned, then it is painted on the same layer, within its parent stacking context, as positioned elements with stack level 0.
Then if you check the painting order
- All positioned descendants with 'z-index: auto' or 'z-index: 0', in tree order
From the below, we can understand than an element with opacity less than 1 will be painted like positionned elements and those element are painted at the step (8). Your other elements have nothing special and they didn't create a stacking context so they will be painted before:
- For all its in-flow, non-positioned, block-level descendants in tree order:
You will have the same result if you replace opacity with position:relative
for example
.element {
width: 500px;
height: 100px;
background: orangered;
}
.child {
height: 150px;
width: 100px;
background: grey;
position:relative;
}
.sibling {
width: 400px;
height: 100px;
background: skyblue;
}
<div class="element">
<div class="child"></div>
</div>
<div class="sibling"></div>
And if you make the sibling
element positionned it will also get painted in the step (8) and will be above the .child
because we follow the tree order.
.element {
width: 500px;
height: 100px;
background: orangered;
}
.child {
height: 150px;
width: 100px;
background: grey;
opacity:0.5;
}
.sibling {
width: 400px;
height: 100px;
background: skyblue;
position:relative;
}
<div class="element">
<div class="child"></div>
</div>
<div class="sibling"></div>
To conclude, the creation of stacking context is only relevant when having child elements since this will oblige them to be painted inside that stacking context then we consider the painting order to see when each element will be painted.
Related questions for more detail:
Why can't an element with a z-index value cover its child?
Why does position:relative; appear to change the z-index?
Upvotes: 3
Reputation: 100
Although you have your answer but if you don't want to decrease the height of .element
you can add one more css property to it which is overflow: hidden
then .child
will not overflow from .element
.
.element {
width: 500px;
height: 100px;
background: orangered;
overflow: hidden;
}
.child {
height: 150px;
width: 100px;
background: grey;
opacity: 0.5;
z-index: 1;
}
.sibling {
width: 400px;
height: 100px;
background: skyblue;
z-index: 2;
}
<div class="element">
<div class="child"></div>
</div>
<div class="sibling"></div>
Upvotes: 1