Stacking order of elements affected by opacity - css

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>

Related

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.

Why transform:translatex in this css class does not move the div sideway?

I want to move a div across a screen. The div is set to width:100% initially. But when the transformation starts, the size of the div seems to have shrinked (the blue border just wraps around the text). Why it is the case?
Before
After
Here is my code example
http://codepen.io/kongakong/pen/MKzmMm
Javascript:
$(document).ready(function () {
init();
});
function init() {
$('input').on('click',
function () {
$('.answer-text-2').closest('.answer').addClass('textout');
$('.answer-hide').addClass('textin');
});
}
css
.row {
}
.hidden {
display: none;
}
.answer-text-2 {
color: red;
opacity: 1;
margin: auto;
}
.answer-text-2-new {
color: blue;
opacity: 0;
margin: 500;
}
.textout {
/* display: none; */
margin: -500;
opacity: 0;
width: '50%';
transition: all 3s ease-in-out;
}
.answer-hide {
border: 1px solid blue;
width: '100%';
}
.textin {
position: absolute;
margin: 0;
left: 0;
transform: translateX(10000px);
transition: transform 5s ease-in-out;
}
Html
<div class="contrainer">
<div>
<input type='button' value="test"></input>
</div>
<div class="row">
<div class="answer">
<span class="answer-text-2">To disappear</span>
</div>
<div class="answer-hide">To move sideway
</div>
</div>
</div>
Well... Your width of a div called answer-hide is set to auto, and it's position is set to static (as for default values). When applying transformation div's position is automatically changed to absolute (as defined in spec) and width:auto for block elements positioned absolutely is something like shrink to fit. To fix that, be sure, that your width property is properly set, because width: 100%; is not equal to width: '100%'; :)
.answer-hide {
border: 1px solid blue;
width: 100%;
}
At the beginning, you use
width: '100%';
This is not a valid value, because width expects a length, not a string. So it's ignored.
Therefore, it has the initial width: auto. For block-level, non-replaced elements in normal flow this is the width of the containing block (subtracting margins, paddings and borders).
Then, you use
position: absolute;
For absolutely positioned, non-replaced elements, when width and right are auto, and left is not, the width is given by the shrink-to-fit algorithm (also called fit-content).
That's why it shrinks.

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);
}

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.

Making parent's background to be on top of childrens in CSS

I have the following DIV structure:
<div id="parent">
<div id="child"></div>
<div id="child2"></div>
</div>
I want to apply one half opaque background into the parent DIV, and one fully visible background to the child DIVs. However, it seems that the child will take over the parent, so, I have now no idea how to come over with this.
EDIT: Some more clarification.
I have a jQuery draggable "window" made of DIVs. Inside it, I have a progress bar with
relative position like:
position: relative;
left: 16px;
top: 16px;
This way the progress bar will be at 16-16 of the window (not the screen) and the progress bar moves correctly along with the window.
However, the progress bar has texture on the top. Take a look at this example:
<div style="background: url('texture.png'), url('empty.png'); width: 256px;">
<div style="background: url('progress.png'); width: 33%;"></div>
</div>
There's an opaque texture applied to the whole progress bar element, for example, if the percentage of this progress bar is 33%, then it looks like xxx------ where x denotes the flowing green bar and - is empty. The texture must be applied to both x and -, but currently the image of x takes place over the texture.
I can't really use Z-index and/or position absolute to position the child elem on the top, because of the relative positioned approach.
I don't know whether I understoood your question correctly, but aren't you looking for CSS3 RGBA colours?
p { color: rgba(0,0,255,0.5) } /* semi-transparent solid blue */
p { color: rgba(100%, 50%, 0%, 0.1) } /* very transparent solid orange */
Reference: 4.2.2 RGBA color values
Here is the progress bar code I use:
To change the percentage, just change the cover class' postiion (e.g. left:80%) and of course the text percentage both of which are in the HTML. Also, it uses a semi-transparent png for the bar image, so you can change the bar background color #888888 in this case to match whatever color you desire.
Note: the files are hosted on tinypic and it's been a little slow for me lately, so give it a few extra seconds to see the images.
CSS
.wrapper {
background: transparent url(http://i50.tinypic.com/2a65xtf.png) no-repeat scroll 0pt 0pt;
width: 216px;
height: 25px;
position: relative;
}
.bar {
background: #888888 url(http://i49.tinypic.com/2cdzyj9.png) repeat scroll center center;
overflow: hidden;
position: absolute;
display: block;
width: 200px;
height: 15px;
top: 6px;
left: 8px;
text-indent: -30px;
}
.cover {
background: transparent url(http://i47.tinypic.com/zyfq61.png) repeat-x scroll 0pt 0pt;
position: absolute;
display: block;
width: 200px;
height: 15px;
top: 0px;
}
.bartext {
position: absolute;
display: block;
top: -0.2em;
font-size: 12pt;
font-weight: bold;
color: #ffffff;
}
HTML
<div class="wrapper">
<span class="bar">
<em class="cover" style="left:50%">
<span class="bartext">50%</span>
</em>
</span>
</div>
Since the children are divs, they will fill to the maximum width they can, which so happens to be the width of the parent. As a result, child and child2 will cover all the area the parent fills. To get some of the parent to show around the children, try setting the size of the children to something less than that of the parent, or try adding padding to the parent.
this is the solution for IE, the bold pieces of code are the magic ones:
<style type="text/css">
#parent { background: red; opacity: .5; filter: alpha(opacity=50); width: 100px; height: 100px }
#child1, #child2 { margin: 10px; position: relative }
#child1 { background: blue }
#child2 { background: green }
</style>
<div id="parent">
<div id="child">lorem
<div id="child2">ipsum
</div>
To be cross-browser I would suggest using an alpha PNG in the parent's background, making life much easier.

Resources