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.
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 has five pos sible values: static
, relative
, absolute
, fixed
, and sticky
. Setting one of these values always serves two purposes:
It specifies the positioning model to be used for the element. That is, what method does the browser use to determine where this element goes?
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.
.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.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
.
.relative-example .positioned-element
, and change this square to background-color: lightblue;
position: relative;
left: 100px;
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.
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.
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.
.absolute-example
, which is the class for this entire section.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.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.
position: fixed;
.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.
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.
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.
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.
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.
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.