Z-Index And The CSS Stack: Which Element Displays First?

Z-index is another css property that appears simple on the surface, but has some deeper rules that can cause confusion.

On the surface it may seem as simple as a positive z-index sits in front of a 0 z-index, which sits in front of a negative z-index. While that is true, there’s more to the overall equation about how a browser decides which elements display in front of others.

In last week’s post on css positioning I touched on the z-index property and mentioned it was more complex than my brief description allowed and that the details would be for another post. This is that more detailed post.

Note: z-index only works on positioned elements so if this is new to you, I suggest reading the positioning post linked to above first and then coming back here to learn about z-index.

What is the Z-index?

Let’s start with a quick review. If you read the previous css positioning post you can probably skip to the next section.

A web page is a two dimensional plane with width and height. Z-index is the missing third dimension, depth, with an axis that moves in and out of your monitor. Z-index allows us to break out of the plane of the page and stack elements on top of each other.

z-index.png

A higher z-index moves toward the front of the page. It wants to reach out of the monitor and into your living room. A lower z-index recedes toward the back of the page away from your living room. If two elements are positioned to occupy the same space in the two dimensional plane and one has a z-index of 10 while the other has a z-index of 5, we would expect to see the z-index 10 element in front of the z-index 5 element.

The z-index property can take one of 3 values:

  • auto(default) Sets the stack order of the element to be equal to the stack order of the element’s parent.
  • number – Can be a positive number, a negative number, or 0. Sets the stack order for the element
  • inherit – Sets the z-index to the same as that of the parent element

The quick definitions of the values above are only so useful. To really understand what’s going on we need to discuss the stack.

sour like a stone
Creative Commons License photo credit: Demion

Stacking Contexts and Stacking Levels

Every css box has three dimensions along the horizontal, vertical, and z axis. When a browser is rendering your design elements along the z-axis it has to decide which element to draw on the canvas first. This order is described in terms of stacking contexts and stacking levels. You can think of a stacking level as being a new layer on the page, but what about this stacking context.

Stacking contexts can be a little confusing.

The root element (html) forms the root stacking context. Other stacking contexts are generated by any positioned element (including relatively positioned elements) having a computed value of ‘z-index’ other than ‘auto’. Stacking contexts are not necessarily related to containing blocks.

w3.org

Sure that’s clear.

The basic idea is there is one stacking context created by default with any web page. The root of that stacking context is the html element and elements inside that stacking context (everything inside the page) are located on different stacking levels within that default context.

When you add css positioning to an element and give it a z-index (other than auto) it creates new stacking context with new stacking levels inside the new context.

The z-index value creates a new integer stacking level for that element with a position along the z-axis set relative to the other boxes within the same stacking context.

Z-index is only one type of stacking level within a stacking context and the levels are rendered according to a set of stacking order rules, which will get to next.

More Water Bottle Caps
Creative Commons License photo credit: Incase Designs

Stacking Order

I know some (or maybe all) of the above discussion on stacking contexts and stacking levels is confusing. Hopefully you’re still with me, because the stacking order helps tie things together.

To understand the stacking order let’s build up the rules slowly by not considering z-index at all at first. In fact let’s look at only two levels in the stacking order to start and then see where new levels fit in the order

  1. Background and borders of the root element of the stacking context
  2. Descendant block boxes in the normal flow, in order of appearance (in the html)

stack-1.png

Remember the root of all root stacking contexts is the html element. Looking at the above you’ll see that the background and borders of the html element represent the lowest level of the stack. A css block box (a div inside your html for example) would be at a higher level within the stack.

Makes sense. If you add a background to your html and then fill a div with content inside your html, you’d expect the div content to be in front of the the html background.

Let’s start adding more levels to the stacking order

  1. Background and borders of the root element of the stacking context
  2. Descendant block boxes in the normal flow, in order of appearance (in the html)
  3. Descendant positioned elements, in order of appearance (in html)

stack-2.png

Positioned elements without z-index applied or z-index: 0 are on a higher stacking level than non-positioned elements. Even if you don’t specify a z-index (default becomes auto) your positioned element will appear on a higher layer than block level elements in the normal document flow.

  1. Background and borders of the root element of the stacking context
  2. Descendant block boxes in the normal flow, in order of appearance (in the html)
  3. Floating blocks
  4. Descendant positioned elements, in order of appearance (in html)

stack-3.png

Floated elements have a higher stacking level than block level elements in the normal document flow, but a lower stacking level than positioned elements without z-index applied.

  1. Background and borders of the root element of the stacking context
  2. Descendant block boxes in the normal flow, in order of appearance (in the html)
  3. Floating blocks
  4. Descendant inline boxes in the normal flow, in order of appearance (in the html)
  5. Descendant positioned elements, in order of appearance (in html)

stack-4.png

Notice that inline boxes have a higher stacking level than both block boxes and floated boxes, which you might not expect.

Now lets put the z-index back in and get the full stacking order rules as define by the w3C.

  1. the background and borders of the element forming the stacking context.
  2. the stacking contexts of descendants with negative stack levels. (negative z-index)
  3. a stacking level containing in-flow non-inline-level non-positioned descendants. (block level boxes in the normal document flow)
  4. a stacking level for non-positioned floats and their contents. (floated boxes)
  5. a stacking level for in-flow inline-level non-positioned descendants. (inline boxes)
  6. a stacking level for positioned descendants with ‘z-index: auto’, and any descendant stacking contexts with ‘z-index: 0’.
  7. the stacking contexts of descendants with positive stack levels. (positive z-index)

stack-5.png

A few things stand out in the final stacking order. As you might expect an element with a positive z-index appears at the top of the stack and while not specifically mentioned the higher the z-index the higher the level in the stack order.

What is less expected is that boxes with negative z-indexes are still higher in the stacking order than the background and borders of the element forming the stacking context. Remember that element forming the stacking context is likely the element you just added z-index to.

Also it’s interesting where boxes with a z-index of 0 or auto lie in the stacking order. There are several stacking levels below your z-index: 0 element, which you might not expect.

The main thing to understand about the stacking order is that more than the z-index value goes into determining what layer in the stack an element is on.

Browser Issues with the Stacking Order

In a perfect world we’d be done. It’s not a perfect world as you know and in the context of z-index it’s browsers that are imperfect. I mentioned two of these in my last post, but they bear repeating

1. Select boxes in IE6 – In IE6 a select box <select> will always appear at the top of the stack. You can set all the z-indexes you want, but the select box will still be at the top.

2. E6 and IE7 have another z-index issue with stacking context. IE looks to the outermost parent with positioning applied to determine which group of elements is at the top of the stack instead of looking at each individual element.

{code type=html}


{/code}

You would expect the paragraph to be at the top of the stack, because it has the highest z-index. However IE6 and IE7 would place the image on top of the paragraph, because it sees two different stacks. One for the div and one for the image. The image has a higher z-index than the div and so will sit on top of everything inside the div.

3. Firefox 2 and negative z-index – FF is not immune from browser issues. Firefox 2 always places an element with a negative z-index behind the entire stacking context instead of above the background and borders of the root of the stacking context.

Summary

Z-index is a little more complex than it first appears. It gets even more complex when you try to explain it. I hope I didn’t confuse you in trying to explain how z-index works.

The main points to remember are that css uses stacking contexts and stacking levels within each of those stacking contexts. CSS also describes a stacking order for which level appears in front of or behind the others around it. Z-index accounts for 3 stacking levels, but there are other stacking levels as well.

By default your html element creates a stacking context and then each time you use css positioning and give the element a z-index you create a new stacking context with it’s own stacking levels and stacking order. While not too difficult to understand where one element will display inside one stacking context, it can be somewhat confusing when comparing different levels in different stacking contexts.

The more positive the z-index the more in front the element appears. The more negative the z-index the further away it appears. However other stacking levels are intertwined with the three z-index stacking levels.

If you’ve ever tried to use z-index and found things not quite working like you expected the answer to why is likely in the stacking order (or occasionally an imperfect browser)

Questions?

« »

Download a free sample from my book, Design Fundamentals.

31 comments

  1. I think the point you make about errors with IE browsers may be misleading and here’s my understanding about why:

    In the example snippet you provide, all browsers correctly place the image above the paragraph because the (with paragraph’s inside) and the image share the same stacking context. It’s as though there is a a parent enclosing all your code with any z-index value. The value doesn’t matter because this parent is setting the stacking context for ALL its descendants.

    Okay, what we need then to demonstrate IE’s typically wayward behaviour is a real parent and let’s assume it is given a z-index of 69 (any old number will do).

    If you leave the rest of your code snippet unchanged then all browsers will continue to place the image above the text.

    But now remove the z-index from the div with the paragraph. This forces browsers to give that div the default value for z-index which is “auto”.

    It is this that IE gets wrong. It treats auto as though it were a value of 1 and (crucially) also establishes a new stacking context. The div and the image now no longer share the same stacking context as far as IE is concerned. This leaves the image above the paragraph.

    Standards compliant browsers will treat the auto attribute correctly meaning that it inherits the z-index of its parent and NO new stacking context is created. The result then is the text above the image.

    Confusing isn’t it! And sometimes the act of trying to explain can confuse further. In case I have done that then try a simpler analysis to show IE’s mistreatment of “auto” and change the z-index value on your from zero to auto and check it out.

    • Thanks Mark. I’ll have to test some code across browsers. The code in the post is more from memory than testing so it’s always possible I got it wrong and your explanation does make a lot of sense.

      I had thought the code I used would cause the problem, but now I’m thinking you’re right and it’s the z-index: auto being treated as z-index: 1 in IE that causes the problem. Maybe I was thinking more about the fix, which would be to increase the z-index on the div to be more than the z-index on the image. Of course that assumes it would still work like you want in other browsers.

      It definitely gets confusing. You can read through the specs and even test different code and sometimes you still have no idea why something is happening.

    • Thanks for pointing that out. I wasn’t aware of the issue. Do you know if it’s specific to certain browsers or is it a general issue affecting all browsers?

      As far as I know z-index should work the same on statically positioned elements. Should being the operative word. The spec says it should work at least.

      If you can offer more details or point me somewhere that talks about this more it would be appreciated.

      Thanks again.

  2. Even though this post is a bit older already, I felt I had to intervene.
    First of all your code snippet is missing the most important point:
    There has to be a positioning for elements to which a z-index is assigned to make it work. You might consider that a matter of course but in a treatise or tutorial this should be apparent in the code snippet.
    The position has to be other than static.
    With that said we come to point two:
    noone is wrong when stating “It may be worth mentioning that declaring position:static on an element does not register its z-index correctly.” since this is how it has to be.
    Position static is the default position and is not supposed to take any z-index.

    The z-index and stacking context issue is a widly misunderstood topic and there is a lot of texts with wrong theses on the internet.
    Not at least this one: https://developer.mozilla.org/en/Understanding_CSS_z-index/Stacking_and_float
    You can easily verify the results by using the provided code and test it.
    e.g. in Firefox 4.x the DIV#3 appears above the DIV#1 as you can see here: http://jsfiddle.net/noRiddle/MntVN/

    I’m currently working on a bigger test case with all kinds of different set ups to finally verify how z-index really works.
    It is certainly much more complicated than many assume,
    not mentioning browser issues.

    noRiddle

    • You might have missed it, but this post is a follow up to one on positioning.

      I don’t see why I specifically needed to show anything other than the z-index in the snippet, since it’s only there to illustrate a point with z-index. If the positioning needs to be shown then so would all the other code to create whatever layout is being created.

      The snippet is just to illustrate one point and so only the code needed to illustrate that point was used.

      • “I don’t see why I specifically needed to show anything other than the z-index in the snippet, since it’s only there to illustrate a point with z-index.”

        While I understand your point as an author concerned with organizing your content in the most logical manor possible, your view about including positioning makes sense.

        However as a reader looking for information on making zindex work I kind of agree with noRiddle and think even a small blurb at the beginning noting that zindex won’t work without applied positioning would be beneficial to those still learning (like me).

        Now armed with that key bit of information I am off to read your positioning article.

        Thank you for taking the time to create these articles in the first place. Please don’t take this as a criticism and consider it me sharing my personal learning experience with the end goal of helping improve the already great work you have done.

        • Point taken. I added a note at the top of the post that z-index needs positioning to work and suggesting people read the positioning post before this one, if this subject is new to them.

          Does that work?

  3. OMG! Steven – You rock!!!

    Thank you so much for taking the time to write this post – I’ve been stuck on this for the last 5 hours, read a dozen articles, and NO ONE even mentioned ‘Context’ – – –

    After five hours, all I needed to do was move one div down one line (including the positioned and z-indexed element) in the preceding Context, and Viola! Like magic my stack orders lined right up πŸ™‚

    Thank you so much for sharing (and simplifying) the concept – I was pulling my hair out over here until I read your post, then five minutes later I had it all sorted out πŸ™‚

    • Glad I could help Elly. I know it was a frustrating 5 hours (believe me I’ve had those same 5 hours myself many times). On the positive side I bet you never have the problem again for more than a minute or two. πŸ™‚

      That context thing and the stacking order used to throw me off at times too. Most of it works as you’d probably expect even if you never read anything about them, but a few points are entirely intuitive.

  4. Thank you so much for this incredible article. I was stuck in the redesign of my website, but thanks to you I finally managed to figure out how to stack those divs properly. Hours of trying and failing all over now thanks to you! Keep up the good work! πŸ™‚

    • Thanks. I’m glad you liked the article. That’s a great image from Mozilla. Definitely more complex than my images. πŸ™‚

      I don’t have anything better at the moment, but maybe I can think of something and create a new post around it.

  5. Nice article. I do have a question though: how would you go about using z-index in css stylesheets that are reused across many projects?

    Assume you have a base-stylesheet that you use as a base for all your projects. This stylesheet contains styling rules for basic functions like forms and buttons and popups. Your application then has a stylesheet that adds some features and maybe overrides some of the base-stylesheet.

    Now, popups have a tendency to be modal, they block the page. So with a little position:-ing and maybe a dash of JavaScript you’re good. But what if you have style elements on the page with a z-index? They will appear _above_ the popup. The solution is to add z-index to the popup, but how high should the z-index be? (it depends on all the other z-index values)

    I’ve thought about solutions and I’ve found 3:

    1. Define ‘ranges’ where z-indexes can go. Application specific z-index overrides and use z-index: 0-100. Feature X will use 100-200. The base-stylesheet will use 1000 and up.

    2. Don’t have any z-index defintions in your base-stylesheet but have a seperate stylesheet that has all the z-index exceptions for the application. The tricky part here is that you need to set all the application specific z-indexes and remember which parts of the base-stylesheet might need a z-index.

    3. Leave all the z-indexing to JavaScript. I haven’t explored this solution yet but I think there are some cases where this might be the only solution. Ex: what if you have a popup over a popup that has z-index elements in it. The highest popup should get the highest z-index value. If that popup too has z-indexed elements they should be higher still.

    The problem I’ve found with z-index is that it doesn’t ‘stack’ like normal elements do (like a tree). If you have an element that has z-index: 2 and a child element that has z-index: 1 that child element will appear _behind_. You would expect it to have a z-index of 2+1 (not 3, just 2 in one stacking layer, and 1 within that). Since the DOM is a tree you would expect z-index to behave like a tree, but it doesn’t. This makes me very sad. This also makes it has to deal with z-index in the case that I describe you need to manually, tediously define every little z-index and you need remember when to go up one. It hard to make abstractions when you’re dealing with z-index. Once you’ve got one place in your stylesheet where you’ve got a z-index, you’ll need to add in every other place.

    What are your thoughts on this?

    • I like your #2 idea, of a separate stylesheet for the z-indexing. I don’t like defining all my z-indexes in JS, as the maintenance can become tedious and then what if its different if I load a new css skin into my app..?

      Currently I’m reviewing our existing CSS, and documenting a much clearer layer model. So rather than use z-indexes of 500, I intend to diagram the different layers, ideally from 1-10, and show what elements appear on what layer.

      Then, anytime I have a full screen overlay, a z-index of 11 will do the trick. Even with our many interactive components, I believe I can have all the components playing nicely within 10 different layers to position them on.

      Let me know what you come up with. Thanks for your post.

    • Frits I take it you received my reply to your email about all of this. I’m glad you now know that z-index does stack like you suspected and it makes your n+1 solution possible.

      Let me know when you get it working.

  6. Excellent article! Really helped me understand while w3 really made an effort to make it almost impossible to understand.

  7. … although the article needs updating in relation to opacity settings of less than 1. This apparently creates a whole new stacking context.

Leave a Reply

Your email address will not be published. Required fields are marked *