Mini-tutorial on responsive layouts, part 2

Jan 4 2018

This is part two of the mini-tutorial. It covers the negative margins, z-index and flexbox. Read part one here.

Positioning (continued)

Negative margins

Another positioning tool is margins with negative values: margin-top: -10px;. One could use a negative margin to:

  • To adjust position of an element, for example, pull it up or left a few pixels. A common use case here is centering an absolutely positioned element with fixed width and height. Have a look at this square block:
div.modal {
  position: absolute;
  height: 100px;
  width: 100px;
}

The initial intuition here can be that giving an element top: 50%; and left: 50%; will center it both vertically and horizontally:

div.modal {
  position: absolute;
  height: 100px;
  width: 100px;
  top: 50%; // center horizontally?
  left: 50%; // center vertically?
}

not really centered

What it really centers, though, is the upper left corner of the block. So, to take into account its dimensions, we use negative margins:

div.modal {
  position: absolute;
  height: 100px;
  width: 100px;
  top: 50%;
  margin-top: -50px; // half of the height
  left: 50%;
  margin-left: -50px; // half of the width
}

centered beautifully

  • To make statically positioned elements overlap. Say, you have several images of playing cards that you want to lay out beautifully in a row:

plain 2D looking cards

By giving each card image margin-right: -90px; we can make the cards overlap, giving depth to the picture:

nicely layed out 3D cards

Surely, the same result can be achieved with position and left properties, but it would be more cumbersome and produce more side effects than the negative margin solution.

As a rule of thumb, remember that applying negative margins on the top and on the left will pull the element itself to that direction, while negative margins on the bottom or right will pull statically positioned neighbours towards the element.

Z-index

Although HTML layouts are flat, elements can overlap and be set on top or underneath each other, like we've shown in the negative margins section. By default, overlapping elements are stacked in this order (bottom to top):

  1. the background and borders of the root element
  2. descendant non-positioned blocks, in order of appearance in the HTML code
  3. descendant positioned elements, in order of appearance in the HTML code

(See stacking without z-index for more info and examples).

However, stacking order can be changed by using the z-index property. The value of z-index is always an integer (positive, negative or zero). Generally, the bigger the z-index value, the "closer" the element is to the viewer. And we could end this discussion right here if it wasn't for the stacking context.

What is stacking context?

The stacking context is a three-dimensional conceptualization of HTML elements along an imaginary z-axis relative to the user, who is assumed to be facing the viewport or the webpage. HTML elements occupy this space in priority order based on element attributes.

(source)

In case this definition didn't help, imagine that there are layers along the imaginary z-axis, each layer represented by a z-index value. Stacking context takes one layer (the layer its root element occupies) and divides it into sublayers, represented by z-index values of the elements in this context. All the elements in this context will be ordered inside the higher layer according to their z-index value, but globally they will stay on the same layer with their root element that created the stacking context.

In an ideal world the one and only stacking context is the HTML document. This is where the gerenal rule of "bigger z-index - closer to the top" works. But more often than not a few other stacking contexts will be formed inside the main one, creating a hierarchy of stacking contexts. When it comes to this, elements from a higher stacking context will always be rendered in front of the elements from a lower stacking context, no matter their z-index.

Here's what the MDN documentation has to say on the matter of the hierarchy of stacking contexts:

The hierarchy of stacking contexts is a subset of the hierarchy of HTML elements, because only certain elements create stacking contexts. We can say that elements that do not create their own stacking contexts are assimilated by the parent stacking context.

And, like it often is, a picture is worth a thousand words. Have a look at this example which explains the concept beautifully.

When a new stacking context is formed

A stacking context is always formed around of the element that meets one of the following conditions:

  • it is a root element of the document
  • it has position: relative; or position: absolute; and z-index other than auto
  • it has position: fixed; or position: sticky;
  • it has transform, filter, perspective properties with value other than none
  • a few other cases listed here

As always with CSS, there's a gotcha (as if all this wasn't enough already). Sometimes, when a new stacking context is formed around an element, it will also become a containing block for all its fixedly positioned children.

Flexbox

Until a while ago, some layouts were extremely hard to implement without using either HTML tables (now considered bad practice) or JavaScript. One example of such layout is a page with three columns of the same width and height, but varying length of content. Like this:

three columns of the same height

That was before a concept of flexible box layout made its way into CSS, which happened quite recently. Flexbox allows for an easier description of parent/child box relations, by giving a parent box control over how its contents should behave.

Two main aspects of the flexbox layout are flex container and flex items. Flex container should have display: flex; (remember the display property?) and all its children (flex items) should have the property flex set. Flex value mainly affects child's size and its relation to the neighboring children. Additionally, there are options for horizontal and vertical spacing of the children, their flow direction, order and many more.

We're not going to go into much detail on the flexbox here, and there are several reasons for that. First, you can find a lot of beautiful flexbox guides and detailed documentation all over the web (some examples are flexbox MDN docs and css-trics flexbox guide). Second, while flexbox does seem like a magical cure to all our CSS problems, it's still fairly new and buggy, not fully supported by all commonly used browsers and lacks in performance. It is, however, useful to be aware of the flexbox and its capabilities.

This is part two of the mini-tutorial. In the third and last part we discuss HTML tables, media queries, cross-browser support and layout best practices.

Masha K.
Software Developer
Back to Blog