16

If we use z-index combined with position: absolute; its possible to place the ::before of a element under itself. There is a good example on another question (jsfiddle.net/Ldtfpvxy).

Basically

<div id="element"></div>

#element { 
    position: relative;
    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 */
}

renders:

enter image description here

So the stacking context/order is defined by z-index. But when I apply z-index: 1; to the element and z-index: -1; to its ::before I cannot achieve the same thing.

Only if I omit z-index from the element.

Any ideas why this is? Is the element rendered after its ::before & ::after pseudos so they get the same z-index?

Working: https://jsfiddle.net/Ldtfpvxy/
Not working: https://jsfiddle.net/Ldtfpvxy/1/ (only added z-index: 1; to element)

Community
  • 1
  • 1
Sergio
  • 28,539
  • 11
  • 85
  • 132
  • Check this [thread](http://stackoverflow.com/questions/3032856/z-index-of-before-or-after-to-be-below-the-element-is-that-possible), Sorry @Sergio, I haven't noticed you already linked it :) – Keammoort Jul 09 '15 at 16:21
  • @Keammoort i linked to that same thread in my question already because it is related. The example is from there. – Sergio Jul 09 '15 at 16:22
  • I am not 100% sure of an explanation and so no adding answer but what I understood from [this article](http://philipwalton.com/articles/what-no-one-told-you-about-z-index/) is that any value for `z-index` (other than default auto) creates a new stacking context and that a child can be positioned behind its parent only when they have same stacking context. Here since both have `z-index` assigned, I assume the stacking contexts are different. – Harry Jul 09 '15 at 16:30
  • It might have something to do with generated content inheriting property:value pairs from parent elements. I would look here: http://www.w3.org/TR/CSS21/zindex.html – TylerH Jul 09 '15 at 16:32
  • Also worth noting that the issue happens when `z-index` on parent has any value. – Harry Jul 09 '15 at 16:36
  • Related, but not a dupe: http://stackoverflow.com/questions/11088176/before-pseudo-element-stacking-order-issue – bfavaretto Jul 09 '15 at 17:32
  • what if my element has absolute:position and z-index:1 like here https://jsfiddle.net/x5x6ye97/1/ – gidzior Oct 06 '16 at 11:50

2 Answers2

17

Your div and its ::after pseudo-element are members of the same stacking context, in this case the root stacking context. The new stacking context you give the pseudo-element would be used as a reference to its children (which are non-existent), but the z-index value applies to the current stacking context. And the CSS spec dictates the following paint order for each stacking context:

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).

Look, child stacking contexts with negative stack levels, such as your div::after are painted before the positioned descendants with stack level 0, such as the div itself. This explains the behavior you noticed.

TylerH
  • 20,799
  • 66
  • 75
  • 101
bfavaretto
  • 71,580
  • 16
  • 111
  • 150
  • 1
    Is there anything to do with the default value `z-index:auto`? As explained on [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/z-index) `auto` - *The box does not establish a new local stacking context.* – Stickers Jul 09 '15 at 17:22
  • 4
    @Pangloss If the div established a new stacking context, then it would not be possible to position any of its children below it. So in this case, yes, the absence of an explicit `z-index` in the div is part of the explanation. But remember that `z-index` alone does not create stacking contexts; they depend on the combination of `z-index` and `position` (or `opacity`). – bfavaretto Jul 09 '15 at 17:28
  • If there is a div set to `position:relative;` and `z-index:0;` (other than *none* or `auto`), so the simple fact is - no matter what any children can never go below it, is that true? – Stickers Jul 09 '15 at 17:34
  • @bfavaretto Of course, `z-index` alone does not do *anything*, let alone not create stacking contexts. – TylerH Jul 09 '15 at 17:39
  • @Pangloss AFAIK yes, that's true for children that create stacking contexts themselves. – bfavaretto Jul 09 '15 at 17:43
  • Let me make sure I understand this: – Deimyts Jul 30 '15 at 15:43
  • 1
    (Accidentally hit enter) In the current example, if only the `::after` has a z-index, the body is rendered at Step 1. The `::after` is rendered at step 2, because it is a child stacking context of the body. The `
    ` is rendered at either step 3 if it is set to `position:static`, but at step 6 if it has an explicit position value. When you add the `z-index: 1` to the `
    `, it is still rendered at step 6, because it is now a child stacking context of the ``. The `::after`, however, is rendered as step 2 of its parent's rendering process, instead of step 2 of the body's.
    – Deimyts Jul 30 '15 at 15:53
  • @Deimyts Exactly. You explained way better than I did. – bfavaretto Jul 30 '15 at 18:01
6

Specifying z-index you are creating a new stacking content;

if this is done only on the child ::after pseudo-elem the parent won't establish a new stacking content and everything will work as expected.

But adding z-index on the parent element will start a new stack (which will also wrap the child-stack..).
And if you look at the first 2 points on stack rendering specification you'll see background will be rendered before other child-stacks:

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. ... ...

here's an example, to clarify the different rendering behavior for nested stacking background.


position: relative is not optional; with the default position:static, z-index has no effect.

Community
  • 1
  • 1
maioman
  • 18,154
  • 4
  • 36
  • 42