Search code examples
htmlcssz-indexfixed

Fixed header&sidebar z-index issue


it's been a while! I am currently trying to solve a problem and I was hoping somebody could help me out. I have a fixed sidebar and a header. Both cast dropshadow. However, I don't want the header to cast shadow on the sidebar (I want them to be on the same level). At the same time, header contains drop-downs and these need to hover over everything. Since it's rather complex, I've created a jsfiddle.

Simple example

I've been forced to paste the code here as well for some reason (SO input validation).

<div class="layout">
  <div class="header z-depth-2">
    <div class="dropdown-toogle">
      <div class="dropdown-menu">
      </div>
    </div>
  </div>
  <div class="page-wrapper">
    <div class="sidebar z-depth-2">
    </div>
    <div class="content">
    </div>
  </div>
</div>

And css

.layout {
  width: 100%;
  height: 100%;
  position: relative;
}

.header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 70px;
  background-color: blue;
  z-index: 101;
}

.sidebar {
  position: fixed;
  top: 70px;
  left: 0;
  width: 200px;
  background-color: green;
  bottom: 0;
  z-index:101;
}

.content {
  padding-left: 200px;
  width: 100%;
  height: 100%;
}

.page-wrapper {
  padding-top: 70px;
  height: 100%;
}

.dropdown-toogle {
  margin-right: 100px;
  float: right;
  position: relative;
  height: 100%;
  width: 50px;
  background-color: yellow;
}

.dropdown-menu {
  position: absolute;
  top: 100%;
  right: 0;
  width: 400px;
  height: 200px;
  background-color: grey;
  z-index:1000;
}

.z-depth-2 {
  box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.3);
}

Notice how when you change view-port width, drop-down slides behind sidebar even when it's z-index is greater.

If I increase z-index of header, it starts casting shadow on the sidebar (drop-down starts working) which I want to avoid. I've been playing with different combination but was unable to sort it out properly.

Hope I managed to make it clear, help much appreciated!


Solution

  • With your current layout this isn't possible. Since the header is set to a z-index behind the sidebar essentially any child of the header will also be behind the sidebar. (Read more on stacking contexts)

    In order to solve this issue what you can do is use an inset box-shadow on the content pane. This will make it so there is a shadow as if it's being cast by the header and sidebar but it's really the container casting it on itself. This way you don't need to worry about the header casting onto the sidebar and the sidebar can safely sit "behind" the header. With this you don't need to fiddle with z-index at all (though I didn't remove them in case it's needed for other things on the page).

    In order to get this to work properly I had to change the padding you were using to position the content element with margin but honestly this is a better property to use for adding space around an element. I recommend you also read up a bit on when to use padding vs margin.

    https://jsfiddle.net/79gykeu3/6/

    HTML

    <div class="layout">
      <div class="header z-depth-2">
        <div class="dropdown-toogle">
          <div class="dropdown-menu">
          </div>
        </div>
      </div>
      <div class="page-wrapper">
        <div class="sidebar z-depth-2">
        </div>
        <div class="content">
        </div>
      </div>
    </div>
    

    CSS

    The thing to note is the .content class now has the box-shadow

    html {
      height: 100%;
    }
    
    body {
      width: 100%;
      height: 100%;
      margin: 0;
    }
    
    .layout {
      width: 100%;
      height: 100%;
      position: relative;
    }
    
    .header {
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      height: 70px;
      background-color: blue;
      z-index: 103;
    }
    
    .sidebar {
      position: fixed;
      top: 70px;
      left: 0;
      width: 200px;
      background-color: green;
      bottom: 0;
      z-index:101;
    }
    
    .content {
      margin-left: 200px;
      width: 100%;
      height: 100%;
        box-shadow: inset 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.3);
    }
    
    .page-wrapper {
      margin-top: 70px;
      height: 100%;
    }
    
    .dropdown-toogle {
      margin-right: 100px;
      float: right;
      position: relative;
      height: 100%;
      width: 50px;
      background-color: yellow;
    }
    
    .dropdown-menu {
      position: absolute;
      top: 100%;
      right: 0;
      width: 70%;
      height: 200px;
      background-color: grey;
      z-index:1000;
    }
    
    .z-depth-2 {
    }