CSS Positioning
|
This section documents general HTML5 and CSS concepts — it is not tied to any specific framework or library. This content was generated with the assistance of AI. Verify it against current MDN documentation and browser-support tables (caniuse.com) before relying on it in production, since HTML/CSS features and browser support continue to evolve. |
The CSS position property controls how an element is placed on the page: whether it stays in the normal
flow of the document alongside its siblings, or is taken out of that flow and placed relative to some other
reference point. There are five possible values — static, relative, fixed, absolute, and sticky — and each one answers two questions differently: does the element still take up space in the document flow
that surrounding elements have to respect, and what is the element’s position measured relative to. Once
an element has any position value other than static, the top, right, bottom, and left properties
(collectively known as the "offset properties") become active, and the element also becomes eligible to
participate in z-index stacking, covered at the end of this page.
static
static is the default value of position for every element. Elements with position: static are laid out
according to the normal flow of the document — block elements stack vertically, inline elements flow
horizontally — exactly as if position had never been mentioned at all. The offset properties (top,
right, bottom, left) have no effect on a statically positioned element.
.box {
position: static; /* the default; rarely written explicitly */
}
relative
position: relative keeps the element in the normal document flow — it still occupies its original space,
and sibling elements are laid out exactly as if the element had not moved — but it lets the offset
properties nudge the element visually away from that original position.
.box {
position: relative;
top: 20px; /* pushes the box 20px down from where it would normally sit */
left: 10px; /* pushes the box 10px right from where it would normally sit */
}
The key detail is that the space the element would have occupied is preserved in the layout — only the element’s own rendered position is shifted. This can leave a visible gap where the element used to be, and can make the shifted element overlap neighboring content.
fixed
position: fixed positions the element relative to the viewport (the browser window) instead of any
ancestor element. A fixed element stays glued to the same point on screen even as the page is scrolled, which
makes it the standard choice for elements such as sticky headers, "back to top" buttons, or persistent
notification bars.
.banner {
position: fixed;
top: 0;
left: 0;
width: 100%;
}
A fixed element is removed from the normal document flow: the space it would have occupied collapses, and surrounding elements lay out as if the fixed element were not there at all.
absolute
position: absolute positions the element relative to its nearest positioned ancestor — that is, the
closest ancestor whose own position is anything other than static. If no ancestor qualifies, the element
is positioned relative to the initial containing block, which in practice is usually the <html> element.
.tooltip-container {
position: relative; /* establishes the positioning context for the child below */
}
.tooltip {
position: absolute;
top: 100%;
left: 0;
}
Like fixed, an absolutely positioned element is removed from the normal document flow: it no longer
reserves any space, and it can overlap other elements as if it were floating in its own layer above the rest
of the page. This is why position: absolute is almost always paired with position: relative on a parent
element — otherwise the child ends up positioned against the whole document rather than the intended
container.
sticky
position: sticky is a hybrid of relative and fixed. The element behaves like position: relative — staying in the normal flow — until the page is scrolled past a threshold defined by an offset property (most
commonly top), at which point it "sticks" and behaves like position: fixed, but only within the bounds of
its containing block rather than the whole viewport. Once the scroll position moves past the bottom of that
containing block, the element scrolls away along with it.
.section-heading {
position: sticky;
top: 0; /* sticks to the top of the viewport once its container reaches this point */
}
Sticky positioning is commonly used for table headers that stay visible while the body scrolls, or for
section headings in a long list that pin to the top while their section is on screen. Unlike fixed, a
sticky element does not collapse the space it occupies in the flow — it behaves exactly like a relative
element until the scroll threshold is crossed.
Comparing the Five Values
| Value | Positioned relative to | Stays in normal flow? | Reserves its original space? |
|---|---|---|---|
|
Normal document flow (no positioning context) |
Yes |
Yes |
|
Its own original position |
Yes |
Yes |
|
The viewport |
No |
No |
|
Nearest positioned ancestor (or the initial containing block) |
No |
No |
|
Its own original position, then the containing block once a scroll threshold is reached |
Yes, until the threshold; behaves as fixed afterward |
Yes |
The diagram below contrasts what happens to two neighboring boxes (Box A and Box C) around a box that is
given each position value (Box B). static and relative keep the three boxes flowing one after another — relative only shifts Box B’s own rendering, leaving a gap and reserved space behind. fixed and
absolute remove Box B from the flow entirely, so Box A and Box C collapse together as neighbors, while Box
B is rendered independently, floating on top of the page. sticky behaves like relative until scrolling
crosses its threshold, after which Box B pins in place while Box A and Box C continue to scroll normally.
Stacking Order with z-index
The z-index property only has an effect once an element’s position is set to anything other than
static — it has no effect on statically positioned elements. z-index governs the stacking order: which
positioned elements render in front of, or behind, one another when they overlap.
.modal {
position: fixed;
z-index: 100; /* renders above elements with a lower z-index */
}
.modal-backdrop {
position: fixed;
z-index: 90;
}
Its value is an integer, which can be positive (rendered above elements with a lower value) or negative
(rendered below them), with 0 commonly used as the baseline. Elements with a higher z-index are painted
on top of elements with a lower one, regardless of their order in the HTML source. Positioned elements
without an explicit z-index stack according to source order, effectively acting as if their z-index were
auto.
z-index is essential for controlling overlapping UI — modal dialogs and their backdrops, dropdown menus,
tooltips, and sticky headers all rely on it to guarantee they render above (or below) the rest of the page,
which is particularly important once animations or transitions cause elements to move and overlap.