CSS Positioning: Static, Relative, Absolute, Fixed, Sticky
A clear guide to the five CSS position values, how they interact with their containers, and when each one is the right choice.
What you'll learn
- ✓What each position value really does
- ✓How absolute elements pick their containing block
- ✓When fixed beats sticky and vice versa
- ✓How to avoid the most common layout bugs
- ✓Practical patterns for tooltips, headers, and overlays
Prerequisites
- •Comfortable with HTML and basic CSS
The CSS position property has only five values, but they trip up developers for years. The reason is that each value changes how the element interacts with its container, its siblings, and the scroll. Once you can describe those interactions in your own words, you stop guessing.
What positioning is and why it matters
Position controls how the browser places an element relative to a reference frame. By default that frame is the normal document flow. By changing position, you opt the element into a different reference frame: its own offset position, a closer ancestor, the viewport, or a scrolling container.
Picking the right value avoids brittle layouts. Picking the wrong one leads to broken tooltips, headers that disappear when you do not want them to, and z-index battles that never end.
Mental model
Think of every positioned element as answering one question: “What do my top, right, bottom, and left properties refer to?” That answer is the containing block. Each position value sets a different containing block.
static -> none. offsets are ignored
relative -> the element's own normal position
absolute -> nearest positioned ancestor (or initial CB)
fixed -> the viewport
sticky -> nearest scrolling ancestor + flow position
The phrase positioned ancestor means any ancestor whose position is not static. That single concept explains ninety percent of absolute positioning surprises.
Hands-on example
Let us build a card with a “New” badge in the top-right corner. The badge should follow the card no matter where the card moves on the page.
<div class="card">
<span class="badge">New</span>
<h3>Pro plan</h3>
<p>Everything you need.</p>
</div>
.card {
position: relative; /* makes the card the containing block */
padding: 1.25rem;
border: 1px solid #ddd;
border-radius: 0.5rem;
}
.badge {
position: absolute;
top: 0.5rem;
right: 0.5rem;
background: #2563eb;
color: white;
padding: 0.125rem 0.5rem;
border-radius: 999px;
font-size: 0.75rem;
}
The card is set to position: relative without any offsets. That has no visual effect on the card itself, but it turns the card into a positioned ancestor. The badge is absolutely positioned and its top and right values are measured from the card, not from the body.
+-----------------------------+
| card (position: relative) |
| [New] | <- badge, position: absolute
| Pro plan |
| Everything you need. |
+-----------------------------+
Now imagine a sticky table header. You want the header to sit normally in the document, but once the user scrolls past it, the header should pin to the top of the table and stay there until the table scrolls out of view.
thead th {
position: sticky;
top: 0;
background: white;
}
Sticky is a hybrid. It behaves like relative until the scroll position crosses the offset, then it behaves like fixed within its scrolling container. The trick is that the containing block is the nearest scrolling ancestor, not the viewport.
Common pitfalls
The biggest is absolute positioning that escapes the wrong ancestor. If no ancestor is positioned, the element latches onto the initial containing block, which is essentially the viewport. The fix is to add position: relative to the intended parent.
A second pitfall is sticky doing nothing. Sticky requires a scrolling ancestor whose overflow is auto or scroll. If you put sticky inside a flex container with overflow: hidden, nothing pins. Also, the sticky element cannot stick past its parent’s bottom edge, so a short parent makes it look like the property is broken.
Fixed elements forget the viewport when an ancestor uses a transform, filter, or perspective. Those properties create a new containing block for fixed children, which means your “fixed to viewport” element starts following the transformed ancestor instead. This bites people building modals inside animated parents.
Finally, z-index without context is a trap. Setting z-index on a static element does nothing. The element must be positioned (or be a flex or grid item with z-index, in modern browsers) for z-index to apply.
Best practices
Reach for relative on parents that need to host absolutely positioned children. It is a cheap, side-effect-free way to set up the right containing block.
Reserve fixed for true viewport overlays like modals, toasts, and floating action buttons. Use sticky for in-flow headers, sidebars, and table headers that should pin only within their section.
Avoid stacking position rules deeply. A component three levels deep with absolute, relative, and fixed mixed together becomes hard to debug. Flatten the structure or use a portal for true overlays.
Pair every positioned overlay with an explicit z-index. Even if the default stacking would work, naming the layer in CSS documents intent.
Wrap-up
The five position values are not interchangeable. Each one redefines the containing block and the relationship to scroll. Once you can name the containing block for any positioned element on the page, sticky headers stop being mysterious, modals stop escaping their parents, and z-index becomes predictable. That single mental shift is the difference between fighting CSS and writing it.
Related articles
- HTML & CSS CSS Flexbox Deep Dive
A focused deep dive into CSS Flexbox: axes, sizing, alignment, growth, shrink, basis, and the small set of rules that explain most layouts.
- HTML & CSS CSS Grid vs Flexbox: When to Use Each (with Examples)
Stop flipping a coin between Grid and Flexbox. Learn the mental model that picks the right layout tool every time, with practical CSS examples.
- HTML & CSS CSS Logical Properties Explained
Replace top, right, bottom, and left with logical block and inline properties that adapt to writing direction automatically.
- HTML & CSS CSS Subgrid Tutorial
Use CSS subgrid to align nested elements with their parent grid, perfect for card layouts, forms, and complex content where rows and columns must line up across components.