11

I have a fixed top navbar with dropdowns. I want to slide the dropdown menus in on hover. I want that the menu is behind the navbar while sliding in. So I've simply tried to set the z-index of both elements which unfortunately did not work for me.

Here a simplified example (codepen)

html

<div class="fixed-top">
  <span class="trigger">hover me</span>
  <div class="menu"></div>
</div>

css

.fixed-top {
  background: #3b5999;
  height: 50px;
  width: 100%;
  padding: 0;
  top: 0;
  position: fixed;
  z-index: 2;
}

.trigger {
  z-index: 2;
  font-size: 33px;
  color: white;
  margin-left: 50%;
}

.trigger:hover + .menu {
  margin-top: 0;
}

.menu {
  z-index: 1;
  height: 300px;
  background-color: #1c7754;
  width: 400px;
  margin: auto;
  margin-top: -400px;
  transition: all 0.75s ease-out;
}

In case it's not clear what I want to do here a simple mspaint sketch ;)

mspaint skills confirmed

3
  • @Justcode my selector is actually working fine :D, your code results in the same as mine
    – boop
    Commented Nov 25, 2015 at 4:39
  • Yes, but when I clicked it wasn't working as working now :P
    – Just code
    Commented Nov 25, 2015 at 4:47
  • 1
    This may help you better understand stacking contexts: stackoverflow.com/a/32515284/3597276 Commented Nov 25, 2015 at 5:07

3 Answers 3

11

This is an extremely common error when beginning to work with stacking contexts in CSS. Basically, you just have to remember that a child cannot exist in a different stacking context from a parent.

So if I have a non-static element (meaning an element with position: anything-but-static [fixed, relative, absolute]), and if that element has no non-static parent element, then it will be at stacking context level 1, no matter where it is in the DOM. Now if that element has a non-static child element, that child will be at stacking context level 2. It cannot be on the same level (level 1) as its parent element. z-index can only affect elements on the same stacking context level, it has nothing to do with elements on different stacking context levels.

The solution is to restructure your HTML, or just use a :before or :after pseudo-element, thus:

.fixed-top {
    height: 50px;
    width: 100%;
    padding: 0;
    top: 0;
    position: fixed; /* The parent: stacking context level 1 */
}
.fixed-top:before {
    background: #3b5999;
    content:'';
    position: absolute; /* stacking context level 2 */
    top: 0; right: 0; bottom: 0; left: 0;
    z-index: 1;
}
.trigger {
    color: white;
    font-size: 33px;
    margin-left: 50%;
    position: relative; /* also stacking context level 2 */
    z-index: 2;
}
.trigger:hover + .menu {
    margin-top: 0;
}
.menu { /* bottom layer -- no stacking context necessary */
    z-index: 0;
    height: 300px;
    background-color: #1c7754;
    width: 400px;
    margin: auto;
    margin-top: -400px;
    transition: all 0.75s ease-out;
}

Note the comments denoting the stacking context levels.

And here's a JSFiddle for an example.

3
  • Nice, still need to change it into this: .trigger:hover + .menu, .menu:hover so that the menu won't slide back up when the mouse leaves the span, +1 Commented Nov 25, 2015 at 5:10
  • @Mi-Creativity I cannot tell that that is part of the spec, but I'll give you kudos for the suggestion.
    – bowheart
    Commented Nov 25, 2015 at 5:13
  • This is the answer I was looking for. I am seeing these symptoms now and after some messing with z-index, I started to feel strongly that children could never go under the parent. I haven't solved it yet, but between your post and your examples, I should be able to.
    – agm1984
    Commented Nov 16, 2017 at 23:59
4

Try running the Code Snippet.

.fixed-top {
  background: #3b5999;
  height: 50px;
  width: 100%;
  padding: 0;
  top: 0;
  position: fixed;
  z-index: 2;
}

.trigger {
  font-size: 33px;
  color: white;
  margin-left: 50%;
  width: 100%;
  height: 50px;
  top: 0;
  position: fixed;  
  z-index: 3;  
}

.trigger:hover + .menu,
.menu:hover{
  margin-top: 0;
}

.menu {
  z-index: 1;
  height: 300px;
  background-color: #1c7754;
  width: 400px;
  margin: auto;
  margin-top: -400px;
  transition: all 0.75s ease-out;
}
<div class="fixed-top"></div>
<span class="trigger">hover me</span>
<div class="menu"></div>

4
  • This is working, thank you. But tbh I'm not very happy with it.. :D related question
    – boop
    Commented Nov 25, 2015 at 5:04
  • Well, would be surprised if someone could achieve it without compromising your html's skeleton :) Commented Nov 25, 2015 at 5:21
  • @kolunar Umm, you mean like how I did in my answer?
    – bowheart
    Commented Nov 25, 2015 at 5:23
  • @bowheart Ah! you nailed it, the pseudo :before masking does the trick :D Commented Nov 25, 2015 at 5:30
0

i think this is what you want.

css code

    .fixed-top {
  background: #3b5999;
  height: 50px;
  width: 100%;
  padding: 0;
  top: 0;
  position : fixed;
}

.trigger {
  z-index: 500;
  font-size: 33px;
  color: white;
  margin-left: 50%;
  position : absolute;
}

.trigger:hover + .menu {
  margin-top: 0;
}

.menu {
  z-index: -9999;
  position : relative;
  height: 300px;
  background-color: #1c7754;
  width: 400px;
  margin: auto;
  margin-top: -400px;
  transition: all 0.75s ease-out;
}

    <div class="fixed-top">
  <span class="trigger">hover me</span>
  <div class="menu"></div>
</div>

here is the reference that you can check

http://www.smashingmagazine.com/2009/09/the-z-index-css-property-a-comprehensive-look/

0

Not the answer you're looking for? Browse other questions tagged or ask your own question.