CSS: Positioning
Control exactly where elements appear on the page.
The position Property
The position property determines how an element is placed in the document. Once positioned, the top, right, bottom, and left properties move it.
| Value | In Flow? | Positioned Relative To |
|---|---|---|
| static | Yes | Normal document flow: top/left/etc. have no effect |
| relative | Yes | Its own original position in the flow |
| absolute | No | Nearest positioned ancestor (not static) |
| fixed | No | The viewport: stays visible while scrolling |
| sticky | Yes (until threshold) | Normal flow until scroll threshold, then viewport |
position: static (Default)
Every element has position: static by default. Elements flow one after another in the order they appear in the HTML. The top, right, bottom, and left properties have no effect on static elements.
position: static
Static is the default. Elements stack vertically as block elements or flow inline. offset properties (top, left, etc.) are ignored.
- Default value: no need to write it explicitly
- top, right, bottom, left and z-index have no effect on static elements
- Elements appear in the order they are written in the HTML
- position: static: also used to explicitly reset a positioned element back to normal flow
position: static
Three static elements stacking in normal document flow.
.box {
position: static; /* default: same as not writing it */
background: steelblue;
color: white;
padding: 12px;
margin-bottom: 8px;
border-radius: 4px;
}position: relative
A relatively positioned element stays in the normal flow, its original space is preserved. The top, left etc. properties move it relative to where it would have been.
position: relative
relative nudges an element from its natural position without removing it from the flow. Its original space is still reserved, other elements do not move to fill it.
- The element stays in the flow: its space is preserved
- top: 20px moves the element 20px down from its natural position
- Most important use: creates a positioning context for child elements with position: absolute
- Without relative on a parent, absolute children escape to the nearest positioned ancestor
position: relative
The shifted box moves but its original space is preserved.
.box {
background: steelblue;
color: white;
padding: 12px;
margin-bottom: 8px;
border-radius: 4px;
}
.shifted {
position: relative;
top: 20px;
left: 30px;
background: crimson;
}position: absolute
An absolutely positioned element is removed from the normal flow. Other elements ignore it and fill the space it left. It is placed relative to its nearest ancestor that has a position other than static.
position: absolute
absolute pulls the element out of the flow and positions it inside the nearest positioned ancestor. If no ancestor is positioned, it uses the initial containing block (the viewport).
- Removed from the flow: other elements act as if it does not exist
- Positioned relative to the nearest ancestor with position: relative/absolute/fixed/sticky
- Pattern: set position: relative on the parent, position: absolute on the child
- top: 0; right: 0 places it in the top-right corner of the parent
position: absolute
A badge anchored to the top-right corner of its parent.
.parent {
position: relative;
background: #f0f8ff;
border: 2px solid steelblue;
padding: 20px;
border-radius: 8px;
height: 80px;
}
.badge {
position: absolute;
top: -10px;
right: -10px;
background: crimson;
color: white;
font-size: 11px;
font-weight: bold;
padding: 3px 8px;
border-radius: 999px;
}position: fixed
A fixed element is removed from the flow and positioned relative to the viewport. It stays in the same place on screen even as the user scrolls, perfect for navigation bars and floating buttons.
position: fixed
fixed elements are anchored to the viewport and never move when the page scrolls. They always stay visible regardless of scroll position.
- Removed from the flow: does not affect surrounding elements
- Positioned relative to the viewport (the browser window), not any parent
- Common uses: sticky navigation bar, floating chat button, cookie banner
- Tip: add margin or padding to the page body so fixed headers do not overlap content
position: fixed
A fixed bar that stays at the top of the viewport while content scrolls.
.fixed-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
background: steelblue;
color: white;
padding: 12px 16px;
font-weight: bold;
z-index: 100;
}
.page-content {
margin-top: 50px; /* offset for the fixed bar height */
padding: 16px;
}position: sticky
A sticky element behaves like relative until the page is scrolled to a defined threshold, then it acts like fixed, sticking in place until its parent scrolls out of view.
position: sticky
sticky is relative until the scroll threshold is reached, then sticks. It requires a top, bottom, left, or right value to define the sticking point.
- top: 0: sticks to the top of the viewport when scrolled to that point
- Stays in the flow, unlike fixed, it does not overlap content below its parent
- The element stops sticking when its parent container scrolls out of view
- Common uses: sticky table headers, section navigation labels
- Does not work if any ancestor has overflow: hidden or overflow: auto
position: sticky
The section header sticks to the top as you scroll inside the section.
.section {
height: 200px;
overflow-y: scroll;
border: 1px solid #ccc;
border-radius: 6px;
}
.sticky-header {
position: sticky;
top: 0;
background: steelblue;
color: white;
padding: 10px 14px;
font-weight: bold;
}
p { padding: 10px 14px; margin: 0; }top, right, bottom, left Properties
These four properties move a positioned element from each edge. They only work when position is set to anything other than static.
top / right / bottom / left
These offset properties move the element from each respective edge. For absolute and fixed, they set the distance from the parent or viewport edge.
- top: 20px: moves element 20px from the top edge of its containing block
- right: 0: flush with the right edge
- left: 50%: moves element so its left edge is at the center
- Setting opposite sides (top and bottom or left and right) stretches an absolute element to fill the space
- inset: 0: shorthand for top: 0; right: 0; bottom: 0; left: 0 (fills the parent)
Offset Properties
Four elements anchored to each corner of the parent.
.parent {
position: relative;
height: 140px;
border: 2px solid #ccc;
border-radius: 6px;
background: #f8fafc;
}
.parent div {
position: absolute;
background: steelblue;
color: white;
padding: 4px 10px;
font-size: 12px;
border-radius: 4px;
}
.tl { top: 8px; left: 8px; }
.tr { top: 8px; right: 8px; }
.bl { bottom: 8px; left: 8px; }
.br { bottom: 8px; right: 8px; }z-index and Stacking Context
When positioned elements overlap, z-index controls which one appears on top. A higher z-index means closer to the viewer.
z-index
z-index only works on positioned elements (not static). A stacking context is an isolated layer: z-index values inside it are compared only against siblings in the same context.
- z-index: 1: above elements with lower or auto z-index
- z-index: -1: behind the normal flow
- z-index only works when position is not static
- Stacking context: created by position + z-index, opacity < 1, transform, filter, etc.
- z-index inside a stacking context is scoped: it cannot escape its parent context
z-index
Higher z-index appears in front regardless of DOM order.
.parent { position: relative; height: 100px; }
.box {
position: absolute;
width: 120px;
height: 60px;
padding: 8px;
color: white;
font-weight: bold;
border-radius: 6px;
font-size: 13px;
}
.a { background: steelblue; top: 10px; left: 10px; z-index: 1; }
.b { background: crimson; top: 25px; left: 50px; z-index: 3; }
.c { background: seagreen; top: 10px; left: 90px; z-index: 2; }Floating (Legacy): float and clear
float was the original CSS technique for multi-column layouts. It removes an element from the normal flow and pushes it to the left or right so other content wraps around it.
float and clear
float was designed for text wrapping around images, like magazine layouts. It was repurposed for page layouts before flexbox and grid existed.
- float: left: element floats to the left; content wraps on the right
- float: right: element floats to the right; content wraps on the left
- clear: both: element moves below all floated elements above it
- clearfix: a technique to prevent a parent from collapsing when all children are floated
float
Text wraps around a left-floated image.
.float-img {
float: left;
margin: 0 16px 8px 0;
border-radius: 6px;
}
.clearfix { clear: both; }Why float is Outdated for Layouts
Float was never designed for page layouts, it was designed for text wrapping around images. Using it for columns required complex hacks like clearfix. Flexbox and Grid solve layout problems cleanly and should be used instead.
Use Flexbox or Grid Instead
Float layouts require clearfix hacks, collapse parent containers, and are fragile. Flexbox and Grid were built specifically for layout and handle alignment, spacing, and responsiveness with ease.
- float collapses the parent container height: requires clearfix workarounds
- Equal-height columns are impossible with float without hacks
- Flexbox: one-dimensional layout, rows or columns
- Grid: two-dimensional layout, rows and columns simultaneously
- Only use float today: for wrapping text around an image (its original purpose)
Flexbox Instead of Float
Three equal columns using flexbox, no clearfix needed.
.flex-layout {
display: flex;
gap: 12px;
}
.col {
flex: 1;
background: steelblue;
color: white;
padding: 16px;
border-radius: 6px;
text-align: center;
}Knowledge Check
1. Which position value removes an element from the normal document flow?
2. An element with position: absolute is placed relative to:
3. Which position value keeps an element fixed to the viewport as the user scrolls?
4. What does position: sticky require to work?
5. What is a stacking context?