Why does z-index: -1; appear above z-index: 1;? - css

Explain this behavior:
<div style="z-index: 1"></div>
<div></div>
<div></div>
<div></div>
div {
position: relative;
background: red;
width: 100px;
height: 100px;
}
div:before {
position: absolute;
background: blue;
width: 100px;
height: 100px;
z-index: -1;
content: "";
left: -5px;
top: -5px;
}
http://jsfiddle.net/2VexH/2/
Only difference is the first div has z-index: 1 set.

Setting a positioned element's z-index to anything other than auto (the initial value) causes the element to generate a new stacking context for its descendant boxes.
This prevents any of its descendants from appearing below it, including the div:before pseudo-element, even if their z-index is negative. Of course, any descendant with a negative z-index will continue to appear below a descendant with a zero or positive z-index within the containing element, but that containing element will always be at the very back.1
The rest of your div elements that don't have a z-index set will use the initial value instead, and therefore not generate stacking contexts for their pseudo-elements, allowing the pseudo-elements to appear below the real elements. The stacking context in which they are drawn instead is that of body.
1 Note that the content of a stacking context root will still appear above the background of a descendant with a negative z-index. This is intentional, and is covered in greater detail in this answer, with relevant links to the spec.

Related

Fixed positioned div inside an absolute positioned div with a scrollbar: bug (?) in Chrome

In the context of the development of a menu, I have a fixed position div (sort of popin that contains level 3 and more menu items) which is contained by an absolute positioned div (that contains level 2 items).
Sometimes the absolute div has a scrollbar and in this case this scrollbar appears above the fixed div on Google Chrome (this doesn't happen on FF and IE).
Simplified jsfiddle example
.level-1 {
background: red;
height: 150px;
width: 200px;
position: absolute;
z-index: 1;
overflow-y: auto;
overflow-x: auto;
}
.level-1-content {
height: 200px;
}
.level-2 {
position: fixed;
left: 50px;
top: 50px;
width: 400px;
height: 200px;
z-index: 2;
background: blue;
}
<div class="level-1">
<div class="level-1-content"></div>
<div class="level-2"></div>
</div>
This issue happens only when the fixed and/or the absolute div have a z-index.
In the jsfiddle simple example, the z-index are not required, but in the context of my menu, I need them.
Does anybody know a CSS solution in order to Chrome not to display this scrollbar above the child div in this context (I mean, with my constraints, i.e. the parent div is absolute and has a z-index and the child div is fixed) ?
Thanks in advance.

What is the default z-index of relative positioned element?

Good afternoon,
I've made a very simple page demonstrating the different position types we can use in css for a new starter at our company, and quite embarrassingly this has exposed a gap in my own knowledge.
I have positioned all elements on the page but I've noticed that my relative positioned element will sit on top of my sticky element when the page is scrolled. It is almost like it has a z-index which is higher than my sticky element - I haven't set any z-index values though.
Is this the correct behavior given code I have provided? Apologies if this is really simple stuff, It has me sitting here scratching my head.
CodePen
body {
font-size: 20px;
font-family: Arial, Helvetica, sans-serif;
height: 3000px;
}
.relative {
width: 20%;
min-height: 200px;
background-color: dodgerblue;
color: white;
padding: 10px;
position: relative;
}
.sticky {
position: sticky;
background-color: green;
color: white;
padding: 10px;
width: 20%;
height: 200px;
top:0;
}
.fixed {
position: fixed;
padding: 10px;
background-color: aqua;
color: black;
height: 200px;
width: 20%;
right: 200px;
top: 300px;
}
.absolute {
position: absolute;
padding: 10px;
right: 0;
top: 0;
width: 20%;
height: 200px;
background-color: red;
color: white;
}
.static {
position: static;
width: 20%;
background-color: blueviolet;
padding: 10px;
color: white;
height: 200px;
}
<body>
<div class="sticky">
This is a sticky div
</div>
<div class="relative">
This is a relative div
</div>
<div class="absolute">
This is an absolute div
</div>
<div class="static">
This is a normal div
</div>
<div class="fixed">
This is a fixed div
</div>
</body>
Yes, this is normal behavior. Your relatively positioned element appears after your stickily positioned element in the source, so its natural stack level is higher and therefore it appears above the stickily positioned element. See section 9.9 of CSS2, or section 11 of css-position.
Stickily positioned elements obey the same stacking rules as relatively and absolutely positioned elements.
If two elements are in the same stacking context (have the same z-index value), then the browser will just look at the order that they are inserted in the dom: the last one would appear on top of the previous.
Here's more info on this topic: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/Stacking_without_z-index
When the z-index and position properties aren’t involved, the rules
are pretty simple: basically, the stacking order is the same as the
order of appearance in the HTML. (OK, it’s actually a little more
complicated than that, but as long as you’re not using negative
margins to overlap inline elements, you probably won’t encounter the
edge cases.)
When you introduce the position property into the mix, any positioned
elements (and their children) are displayed in front of any
non-positioned elements. (To say an element is “positioned” means that
it has a position value other than static, e.g., relative, absolute,
etc.)
https://philipwalton.com/articles/what-no-one-told-you-about-z-index/
Z-index only works on elements that have been position with absolute, relative, fixed. As your sticky element appears before your static element in the HTML, it will take precedence in the stacking order.

Remove mix-blend-mode from child element

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/
The solution on how to avoid mix-blend-mode affects children:
Make child element position relative, give it a width and height;
Create some real or pseudo element inside the child with absolute position, and apply mix-blend-mode to it;
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;
}
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
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/
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>
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);
}

Stacking order of elements affected by opacity

How are z-index and opacity related when deciding stacking order of an element in HTML?
when i keep opacity less than 1 on an element which is having some z-index say 999. The element is going behind the element which is having no z-index.
$(function() {
$("#checkbox1").on("change", function() {
$("#green-parent").toggleClass("add-opacity", this.checked);
});
});
.green,
.blue {
position: absolute;
width: 100px;
line-height: 100px;
text-align: center;
color: white;
}
.green {
z-index: 999999999;
top: 50px;
left: 50px;
background: green;
}
.blue {
top: 60px;
left: 90px;
background: blue;
}
.add-opacity {
opacity: 0.99;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<input id="checkbox1" type="checkbox" value="1">
<label for="checkbox1">Add opacity to green box parent</label>
<div id="green-parent">
<span class="green">Green</span>
</div>
<div>
<span class="blue">Blue</span>
</div>
Positioned elements with a z-index value other than "auto" and elements with an opacity value less than 1 generate a stacking context. Refer to the rules regarding the painting order.
In your first example we have the root stacking context with various descendants including:
positioned green box with positive z-index
positioned blue box with auto z-index
The blue box with auto z-index is placed behind; green box with positive z-index is placed in front (see rule no. 8 and 9).
In your second example we have:
an element with opacity (which contains green box; note that z-index on the green box becomes local to this element)
positioned blue box without z-index
Both elements fall under same category (see rule no. 8). In which case the HTML order determines which element appear in front. The blue box appears later in the source order so it appears in front.
Aside from the opacity stacking context Alexey Ten pointed out in his comment (which is a factor here), the z-index is relative to the element's container. In this case, both your blue and green elements are contained within separate div parents which have no defined z-index. Due to the HTML ordering, the latter div (the one with the blue box) will appear on top of the former one (the green one).
In this below example, I've added the class .first to the first parent div and .second to the second one, then given them their own z-index properties.
.green, .blue {
position: absolute;
width: 100px;
color: white;
line-height: 100px;
text-align: center;
}
.green {
z-index:999999999;
top: 90px;
left: 60px;
background: green;
}
.gp{
opacity:0.99;
}
.blue {
top: 100px;
left: 100px;
background: blue;
}
.first, .second {
position: relative;
}
.first {
z-index: 2;
}
.second {
z-index: 1;
}
<div class="first">
<span class="green">Green</span>
</div>
<div class="second">
<span class="blue">Blue</span>
</div>

Move Layer between Parent and Child (z-index)

I have 3 DIVS. A Parent, a Child and a Layer (sibling of Parent). The Layer should appear between parent and child.
<div class="parent">
<div class="child"></div>
</div>
<div class="layer"></div>
CSS
div {
position: absolute;
width: 100px;
height: 100px;
}
.parent {
z-index: 1;
top: 0;
left: 0;
background-color: red;
}
.child {
z-index: 3;
top: 60px;
left: 60px;
background-color: blue;
}
.layer {
z-index: 2;
top: 30px;
left: 30px;
background-color: green;
}
Here's a JS Fiddle:
http://jsfiddle.net/PHwua/
strangely, i can't get the layer to appear between parent and child. On a live site, this works for some reason in all browsers (IE8-11, FF, Chrome) except Safari.
Now i can't even get the JSFiddle to work.
Your issue probably has to do with the stack(ing) order of HTML elements.
Basically, z-index affects elements inside the same stacking context. The parent and layer boxes are in the same context; so their z-indexes are evaluated first. Then the child box's z-index is evaluated against its stacking context (which nothing else exists in it since its a sub-context of parent).
If you take parent out of the stacking order (by making its position static, for example, or getting rid of its z-index), then child and layer will be in the same stacking context.
Forked JSFiddle here.

Resources