::Before Pseudo-Element Stacking Order Issue

Is it possible to set the stacking order of pseudo-elements below their parent element?

Pseudo-elements are treated as descendants of their associated element. To position a pseudo-element below its parent, you have to create a new stacking context to change the default stacking order.

Positioning the pseudo-element (absolute) and assigning a z-index value other than “auto” creates the new stacking context.

#element {     position: relative;  /* optional */    width: 100px;    height: 100px;    background-color: blue;}
#element::after { content: ""; width: 150px; height: 150px; background-color: red;
/* create a new stacking context */ position: absolute; z-index: -1; /* to be below the parent element */}
<!DOCTYPE html><html><head>  <meta charset="utf-8">  <title>Position a pseudo-element below its parent</title></head><body>  <div id="element">  </div></body></html>

::before pseudo-element stacking order issue

The contents of the div, which include the two pseudo-elements and the p element, participate in the same stacking context (relative to the div). This is because, as you note, all three of them are statically-positioned; in other words, not positioned at all. (Yes, these elements do stack along the z-axis while in flow; you simply cannot manipulate their stack levels using z-index because they're not positioned.)

Here's a summary1 of the order in which the various parts are drawn, bold emphases where relevant to your question:

Each box belongs to one stacking context. Each positioned box in a given stacking context has an integer stack level, which is its position on the z-axis relative other stack levels within the same stacking context. Boxes with greater stack levels are always formatted in front of boxes with lower stack levels. Boxes may have negative stack levels. Boxes with the same stack level in a stacking context are stacked back-to-front according to document tree order.

Within each stacking context, the following layers are painted in back-to-front order:

  1. the background and borders of the element forming the stacking context.
  2. the child stacking contexts with negative stack levels (most negative first).
  3. the in-flow, non-inline-level, non-positioned descendants.
  4. the non-positioned floats.
  5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
  6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
  7. the child stacking contexts with positive stack levels (least positive first).

Since div::before is inserted before the content of the div, and div::after is inserted after it, when they display inline in a static position they'll naturally follow this rule, even if sandwiching a block element (the ordering takes both block boxes and inline boxes into account).

Notice that, for obvious reasons, backgrounds are usually painted first, with the content overlaying them:

  1. The p element, as a block-level element, has a background that's painted first (#3).

  2. The inline-level pseudo-elements are then drawn with their backgrounds over the p background (#5). In the formatting model, they are siblings of the p element, so they all participate in the stacking context of the div and not that of the p.

  3. The div::before pseudo-element (both its content and background) appears behind the p text because it comes before the p in the visual tree.

  4. The div::after pseudo-element (both its content and background) appears in front of the p text because it comes after the p in the visual tree.

As indicated in my comment, if you make the pseudo-elements display as blocks, the background of the div::before will hide behind that of the p element, but not the text; instead, the text of the div::before will be positioned between the background and the text of the p element. Also notice that the background of the div::after is painted in front of that of the p, while the content is painted frontmost. Again, this has to do with backgrounds being painted before, or behind, content in relation to the rules above.


1 A much more detailed description can be found in Appendix E of the spec.

Put Pseudo element behind parent

div { 
position: relative; /* optional */
width: 100px;
height: 100px;
background-color: blue;
}

div::after {
content: "";
width: 150px;
height: 150px;
background-color: red;

/* create a new stacking context */
position: absolute;
z-index: -1; /* to be below the parent element */
}

Pseudo-elements are treated as descendants of their associated element. To position a pseudo-element below its parent, you have to create a new stacking context to change the default stacking order.
Positioning the pseudo-element (absolute) and assigning a z-index value other than “auto” creates the new stacking context.

CSS ::before pseudo-element not respecting z-index stacking context

::after inserts its content immediately before the end of the tag it's applied to but still inside the tag, z-index couldn't be lower than the z-index of the tag, so your approach won't work. But you could do ::after to the parent tag, which in your case has class 'circle'. As the inserted content will be relative to the wrapping tag, you should adjust the position of the contend related to this tag.

See a codepen example:

Codepen Example

See a snippet example here:

* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

.canvas {
background: #573d0e;
height: 100vh;
width: 100vw;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}

.circle {
width: 70vmin;
height: 70vmin;
background: #ffffff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
position: relative;
}

.circle:after {
content: '';
position: absolute;
z-index: 1;
width: 15vmin;
height: 15vmin;
background: black;
border-radius: 50%;
top: 10vmin;
left: 12vmin;
box-shadow: 30vmin 0 #000;
}

.panda {
position: relative;
border: 1.5vmin solid black;
width: 45vmin;
height: 45vmin;
border-radius: 50%;
background: #fff;
z-index: 2;

}

.eyes {
position: relative;

> * {
position: absolute;
width: 11vmin;
height: 15vmin;
background: #000;
border-radius: 70px / 100px;
left: 8.7vmin;
top: 9vmin;
transform: rotate(12deg);

&:before,
&:after {
content: '';
position: absolute;
}

&:before {
width: 4vmin;
height: 6vmin;
background: white;
border-radius: 76px / 100px;
left: 5vmin;
top: 3.2vmin;
transform: rotate(348deg);
}

&:after {
width: 2vmin;
height: 3vmin;
background: black;
border-radius: 76px / 100px;
left: 6.3vmin;
top: 5vmin;
transform: rotate(348deg);
}
}

:last-child {
transform: scale(-1, 1) rotate(12deg);
left: 22.3vmin;
}
}

.snout {
position: absolute;
width: 25vmin;
height: 18vmin;
top: 23vmin;
left: 8.5vmin;
bottom: 5vmin;
background: #fff;
border-radius: 50%;
border: 1.5vmin solid black;

&:before {
content: '';
position: absolute;
width: 1.5vmin;
height: 5vmin;
left: 10vmin;
top: 7vmin;
background: black;
}
}

.nose {
position: absolute;
width: 10vmin;
height: 7vmin;
left: 5.7vmin;
top: 0.5vmin;
background: black;
border-radius: 50% 50% 48% 52% / 21% 21% 79% 79%;
}

.mouth {
position: absolute;
width: 9.6vmin;
height: 5vmin;
border-radius: 50%;
border-bottom: 1.5vmin solid black;
bottom: 1.6vmin;
left: 6vmin;
}
    <div class="canvas">
<div class="circle">
<div class="panda">
<div class="eyes">
<div></div>
<div></div>
</div>
<div class="snout">
<div class="nose"></div>
<div class="mouth"></div>
</div>
</div>
</div>
</div>

Z-index with before pseudo-element

The ::before pseudo-element is placed inside the header element.

CSS Spec:

The :before and :after pseudo-elements interact with other boxes as if they were real elements inserted just inside their associated element.

Setting the z-index for the header element creates a new Stacking Context, so the new pseudo element you created can not float behind the header element, because it would have to go outside that Stacking Context.

I suggest that you simply precede the header element by another element, so it stacks correctly by default. Example.

z-index doesn't work with ::after/::before element?

You can remove z-index from parent element and use negative z-index: -1 on pseudo element. If you want only green line on red line you need to remove white background from parent also DEMO

.or {  border: 2px solid #8fc300;  border-radius: 5px;  color: #333;  font-size: 17px;  font-weight: 600;  height: 34px;  line-height: 26px;  text-align: center;  width: 34px;  margin-top: 64px;  margin-left: 20px;  margin-right: 20px;  background: #fff;  /*For z-index - keep the green area on top*/  position: relative;}.or::after {  background: red;  content: "";  display: block;  height: 116px;  margin-left: 15px;  margin-top: -68px;  width: 4px;  /*For z-index - keep the green area on top*/  position: absolute;  z-index: -1;}
<div class="or"></div>

Changing stacking order for CSS pseudo element

As far as I understood you need this

#progBar {z-index: -2;}
#cOne::after { z-index: -1;}

    #progBar    {    z-index: -2;     background-color:#bdbdbd;     padding:1.5vw;       position:relative;      height:9vw;    }    .progcapt    {     background-color: #526cfd;     color: transparent;     text-shadow: 0px 2px 3px #526cfd;      -webkit-background-clip:text;     font-family:'arial black';     font-size:3vw    }    #cOne    {     position:absolute;     left:calc(50% - 2.5vw);     top:calc(50% - 2.5vw);     border-radius:5vw;     height:5vw;     width:5vw;     border:1px solid #526cfd;     text-align:center;     display:flex;     align-items:center;     justify-content:center;     box-shadow:0px 0px 3vw #526cfd;     background-color:white;
} #cOne::before { position:absolute; width:50vw; height:1vw; background-color:rgba(82,108,253,0.5); content:''; z-index:-1; } #cOneRing { position:absolute; top:-calc(0.5vw + 1px); left:-calc(0.5vw + 1px); width:6vw; height:6vw; border:1px solid #526cfd; border-radius:6vw; }
     <div id='progBar'>        <div id='cOne'>         <span class='progcapt'>1</span>         <div id='cOneRing'> </div>        </div>      </div>

z-index issue while transforming after pseudo-element with transition

You are having an issue because the transform is creating a stacking context moving the pseudo element inside thus it can not more be behind the main element.

I would advise you to consider something else because relying on the pseudo element behind its parent element may create issue because the pseudo element can go behind other element.

Here is a basic example if I add a background to the body where you will partially see the pseudo element because it's behind the body.

button{  background-color:red;  padding:10px 15px;  border:none;  transition: .5s;  position:relative;  color:white;  bottom: 0;   right: 0;}
button:hover{ bottom: -5px; right: -5px;}
button:hover:after{ bottom:0; right:0;}
button:after{ content: ''; transition:.5s; position:absolute; bottom:-5px; right:-5px; z-index:-1; width:100%; height:100%; background-color:blue;}
body,html { background:#fff;}
<button>test</button>

z-index not working properly with pseudo-element

That is because you have positioned your pseudo-element absolutely in the <a> element, and this causes it to be stacked on top of the text node, which has static positioning.

To solve this issue, simply wrap the inner text with <span> and then add this rule:

.start-coding > span {
position: relative;
z-index: 2;
}

Even better: you don't even need to add z-indices to both ::before and the nested <span>, since they come in an order that ensures that, when they are placed in a stacking context, the <span> will always be on top (since it comes after the ::before element in the DOM tree). See example: