Search code examples
htmlcssuser-interfacez-index

Z-Index does not apply to border-right when width set on inner-div


This has me stumped. I applied a border-right, border-left, and border-bottom to a div which has another Green BG color div inside of it. I set the z-index: 1 and position: relative on the parent div with the borders.

However, as you can see in the photo, only the LEFT border gets the z-index applied to it, whereas the RIGHT border doesn't get the z-index and falls behind the green div.

This ONLY happens if I have a width set on the inner-div wider than the outer div, and it only seems to prevent the first border-right from coming above the bg, as a repeating element doesn't suffer from this issue.

This makes no sense, and it's consistent across Chrome, Firefox, and Edge.

Is this some weird quirk / bug with CSS that I just discovered? Or am I somehow completely messing this up?

.flex-div {
  display: flex;
}

.outer-div {
    position: relative;
    border-right: 7px solid #cccccc;
    border-bottom: 1px solid #cccccc;
    border-left: 7px solid #cccccc;
    z-index: 1;
    max-width: 200px;
    flex: 0 0 200px;
}

.mid-div {
  width: 600px;
  z-index: -1;
  position: relative;
}

.inner-div {
    background-color: #00b989;
    width: 250px;
    height: 50px;
    display: inline-block;
    z-index: -1;
    position: relative;

}
<div class="flex-div"> 
  <div class="outer-div"> outer
    <div class="mid-div"> 
      <div class="inner-div">inner</div>
      <div class="inner-div">inner</div>
    </div>  
  </div>
  <div class="outer-div"> outer  </div>
  <div class="outer-div"> outer  </div>
<div>

Could you help me diagnose the reason why this is happening, and any workaround to get the borders on both the left and right of the outer-div to overlay the -inner-div?

z-index


Solution

  • To fix your issue you need to apply z-index to child elements not to parent element (keep the parent with z-index auto)

    All the border on the top:

    .flex-div {
      display: flex;
    }
    
    .outer-div {
        border-right: 7px solid #cccccc;
        border-bottom: 1px solid #cccccc;
        border-left: 7px solid #cccccc;
        max-width: 200px;
        flex: 0 0 200px;
    }
    
    .mid-div {
      width: 600px;
      z-index: -1;
      position: relative;
    }
    
    .inner-div {
        background-color: #00b989;
        width: 250px;
        height: 50px;
        display: inline-block;
        z-index: -1;
        position: relative;
    
    }
    <div class="flex-div"> 
      <div class="outer-div"> outer
        <div class="mid-div"> 
          <div class="inner-div">inner</div>
          <div class="inner-div">inner</div>
        </div>  
      </div>
      <div class="outer-div"> outer  </div>
      <div class="outer-div"> outer  </div>
    <div>

    All the border on the bottom

    .flex-div {
      display: flex;
    }
    
    .outer-div {
        border-right: 7px solid #cccccc;
        border-bottom: 1px solid #cccccc;
        border-left: 7px solid #cccccc;
        max-width: 200px;
        flex: 0 0 200px;
    }
    
    .mid-div {
      width: 600px;
      z-index: 1;
      position: relative;
    }
    
    .inner-div {
        background-color: #00b989;
        width: 250px;
        height: 50px;
        display: inline-block;
        z-index: -1;
        position: relative;
    
    }
    <div class="flex-div"> 
      <div class="outer-div"> outer
        <div class="mid-div"> 
          <div class="inner-div">inner</div>
          <div class="inner-div">inner</div>
        </div>  
      </div>
      <div class="outer-div"> outer  </div>
      <div class="outer-div"> outer  </div>
    <div>


    To understand your initial issue you need to consider painting order and stacking context. By applying z-index to parent element you create a stacking context and since all of them have the same z-index they will be painted considering the tree order. So we print the first element and ALL its content then the second and ALL its content and so on.

    Following this logic you will see the left border of the first and the right one will be hidden due to the overflow of the content then you will see the left border of the second element on the top of all the previous content and so on.

    Here is a basic example to better illustrate your issue:

    .box {
     border-left:10px solid red;
     border-right:10px solid red;
     height:50px;
     width:100px;
     display:inline-block;
     vertical-align:top;
     position:relative;
     z-index:1;
    }
    .box > div {
      width:130%;
      height:60%;
      background:blue;
    }
    <div class="box">
      <div></div>
    </div><div class="box" style="margin-top:20px;">
      <div></div>
    </div><div class="box" style="margin-top:30px;">
      <div></div>
    </div>

    Added some offset to parent element so you can clearly identify the painting order.

    Now if you set positive z-index to only child elements they will all get painted on the top of the all the parent elements.

    .box {
     border-left:10px solid red;
     border-right:10px solid red;
     height:50px;
     width:100px;
     display:inline-block;
     vertical-align:top;
    }
    .box > div {
      width:130%;
      height:60%;
      background:blue;
     position:relative;
     z-index:1;
    }
    <div class="box">
      <div></div>
    </div><div class="box" style="margin-top:20px;">
      <div></div>
    </div><div class="box" style="margin-top:30px;">
      <div></div>
    </div>

    And if you apply negative z-index they will all get painted behind:

    .box {
     border-left:10px solid red;
     border-right:10px solid red;
     height:50px;
     width:100px;
     display:inline-block;
     vertical-align:top;
    }
    .box > div {
      width:130%;
      height:60%;
      background:blue;
     position:relative;
     z-index:-1;
    }
    <div class="box">
      <div></div>
    </div><div class="box" style="margin-top:20px;">
      <div></div>
    </div><div class="box" style="margin-top:30px;">
      <div></div>
    </div>

    Applying any z-index value to parent element will make what you want impossible since the child elements will get trapped inside the stacking context of their parent.


    Related question for more details: Why can't an element with a z-index value cover its child?