Positioning

On every page we've built thus far, the position of every element has been determined by where that element landed in the document flow. Elements at the beginning of the HTML document are positioned at the beginning of the rendered page, then as we proceed toward the bottom of the source code, the finished output is rendered sequentially, more or less like words in word processor. The document flow can be tricky to manage for complex layouts, but it is also incredibly flexible, because it's built around the idea that content determines page height. However much content you have, it always fits!

While relying on the document flow to position elements is almost always the right way to go, it is not the only way. CSS provides us with tools for taking control of (and responsibility for) the position of elements directly.

We can control the position of an element with a combination of the position property, and the top, right, bottom, and left properties.

The top, right, bottom, and left properties

These properties use a simple numeric value, usually expressed in pixels or as a percent, such as left: 100px;. However, these properties are meaningless in isolation: to change anything, the element must also be positioned using the position property, and how the element is changed depends on position as well.

The position property

The position property has five possible values: static, relative, absolute, fixed, and sticky. Setting one of these values always serves two purposes:

Let's look at each possible value in turn.

position: static;

Static is the normal value for html elements. It means the element follows the document flow. Elements that are position: static; are not considered to be positioned: the top|right|bottom|left properties have no effect on static elements. Static elements are not positioning parents: they have no special behavior for positioned descendants.

  1. Create and link a stylesheet for this document.
  2. Create a selector for .static-example .positioned-element, which should select the grey square above. Confirm it's working by setting background-color: green; to change the square's color.
  3. Set left: 100px; for the square. You'll see no change, because it is, by default, position: static;.

position: relative;

Elements set to relative also follow the document flow, but relative elements are different from static elements in two respects. They can be offset from their normal position in the document flow using top|right|bottom|left.

  1. Create a new selector for .relative-example .positioned-element, and change this square to background-color: lightblue;
  2. Set position: relative;
  3. Set left: 100px;
  4. Set top: 50px;

Setting left: 100px; on the element will move the element from the left by 100px, offsetting it from the place it would have normally occupied. Likewise, setting a top value moves the element downward from the top. This offset motion leaves a 'hole' behind. See how there is still a gap where the square started, and see how the text after it behaves as though the square is still there. That is, as far as the elements around it are concerned, the offset element is still where it always was: the browser applies the offset after the elements in the normal document flow are rendered.

Elements with position: relative; also become the positioning-parent for absolutely-positioned descendant elements. More about that in a moment.

position: absolute;

Elements set to position: absolute; are ignored by the document flow, as if they don't exist.

  1. Create a new selector for the square in this section. Make it red, and position: absolute;.

See how the text after the element 'slid up' to fill the space? As far as that text is concerned, the square isn't there anymore, it's been removed from the document flow entirely.

By default, absolutely-positioned elements sit wherever the document flow would have placed them, but setting any of top|right|bottom|left will cause the element to be repositioned.

  1. Add the rule top: 0;. Don't be alarmed when the square disappears!

We have just instructed the square to be repositioned 10px away from the top... but top of what? This is where the idea of the positioning-parent comes into play. The positioning-parent is the nearest ancestor that is positioned. By "nearest" we mean nearest generationally: the element's immediate parent, or grandparent, or great-grandparent, etc. If no ancestor is positioned, the positioning-parent is the document itself. Scroll to the top of this document, and find the red box sitting 0px from the top edge!

Let's give this square a positioning-parent.

  1. Create a selector for .absolute-example, which is the class for this entire section.
  2. Set position: relative; for the section. Note that the section itself doesn't change it's position, because relative still follows the document flow. But it does make this section a positioning-parent.
  3. For the red box itself, set right: 0;. This will position it 0px from the right side of the parent container.

The red box is now being positioned in relation to this section, because this section is its positioning-parent. It should now be pushed right up into the upper-right corner of this section.

Absolutely-positioned elements are themselves also positioning-parents. That is, this element becomes the positioning reference for its absolutely-positioned children, if it has them.

position: fixed;

Elements with position set to fixed are removed from the document flow as well. But rather than being positioned in relation to a positioning-parent, they are positioned in relation to the browser window itself. This means that when the document scrolls, the fixed-position item does not move with the page.

  1. Select the square in this section. Make it purple.
  2. Set position: fixed;.
  3. Set top: 0; and left: 0;.

See how the purple square is now attached to the upper-left corner of the window, regardless of scroll position? We often see this technique used to keep a nav bar at the top of the screen. Let's emulate that.

  1. Add width: 100%; to the selector for the purple square.

We now have a 'nav bar' of sorts, and could put links within it.

As with other positioned elements, setting position: fixed; turns this element into a positioning-parent for its absolutely-positioned children, if it has them.

position: sticky;

Sticky positioning is a hybrid of relative and fixed. A sticky-positioned item will scroll with the page until it hits a threshold specified by top, where it will 'stick', letting the page scroll underneath it. When the bottom of the containing element arrives, it will pull the sticky element off the top of the page with it.

  1. Select the square in this section. Make it orange.
  2. Set top: 50px;

The square should stay right where it started on the page, until you scroll it up to the top. If you keep scrolling, you should see it pull away as this section leaves the page.

As with other positioned elements, setting position: sticky; turns this element into a positioning-parent for its absolutely-positioned children, if it has them.

The z-index property

When we start removing elements from the document-flow, the possibility of having visually overlapping elements is greatly increased. By default, positioned elements will sit 'in front' of static elements, and positioned elements defined later on the page will be in front of earlier positioned elements. However, the z-index property allows us some control over the layering order. It's akin to the 'bring to front' and 'send to back' options in Illustrator or InDesign.

The z-index property uses a numeric value. Positive numbers bring the element 'toward' us, negative numbers push it 'away' from us.

Remember how the blue box in the relative example above covered up some of the text? Let's fix it with z-index.

  1. Find your selector for the lightblue box, and set z-index: -1;.

You certainly also noticed that the orange 'sticky' square passed in front of the purple 'navbar' as it was pulled off the page, which felt wrong. That's because they are both positioned items, and the sticky square came later in the document, so is layered higher by default. We can use z-index to fix that, too.

  1. Find your selector for the purple navbar. Set z-index: 9999;

There's nothing magic about the number 9999, it's just a value that brings the navbar very far forward, with the hope that any other z-indexed items on the page will also pass beneath it.

It's worth noting that the stacking order defined by z-index is organized by 'stacking context.' The details can be learned from a good reference like the Mozilla Developer Network, but the short version is that z-index on a parent brings all of its child elements too. If element A is z-indexed to be in front of element B, then element A's children cannot be z-indexed to be behind element B, no matter what number you give them.

As we conclude, I want to reiterate that using positioning means you are assuming responsibility for how the element is placed on the page, and for making sure the element is appropriately-handled at all page-widths. That can be difficult to do, and for that reason, these techniques should not be considered an alternative to the normal document flow. Instead, think of them as tools for controlling UI elements (like the navbar), or for adding small presentation enhancements within the normal document flow.