CSS position: static, relative, absolute, fixed, sticky
CSS position explained with real examples: when to use static, relative, absolute, fixed and sticky, how offsets and z-index work, and why sticky sometimes fails.

How do you put a badge in the corner of a card, keep a header on screen while the page scrolls, or pin a button to the bottom of the window?
Short answer: with the CSS position property. It has five values. static is the default. relative nudges an element from its normal spot. absolute places it inside the nearest positioned parent. fixed pins it to the browser window. sticky scrolls normally until it reaches a point, then sticks.
By the end of this post you will know what each value does, which one to pick for common layouts, how top, left and z-index fit in, and why sticky sometimes refuses to stick. If margins and padding still feel fuzzy, The CSS box model explained is a good warm-up.
The five values at a glance
| Value | Stays in the normal flow? | Positioned relative to | Typical use |
|---|---|---|---|
static | Yes | Nothing, offsets are ignored | The default for every element |
relative | Yes, its space is kept | Its own normal position | Small nudges, and a reference point for absolute children |
absolute | No | The nearest positioned ancestor | Badges, icons, dropdowns, overlays |
fixed | No | The browser window (viewport) | Cookie bars, chat buttons, back-to-top buttons |
sticky | Yes, until it sticks | Its scrolling container | Section headers, table headers, sidebars |
The offset properties are top, right, bottom and left. They do nothing on a static element, and they mean something different for each of the other values. What Is Positioning (opens in a new tab) introduces the idea.
static: the default
Every element starts as position: static. It sits where the normal flow of the page puts it: block elements stack top to bottom, and inline elements run along a line.
.box {
position: static;
top: 20px; /* ignored */
}You rarely write static yourself. You will mostly see it when resetting an element that was positioned somewhere else.
relative: nudge it from where it would be
relative keeps the element in the flow, then shifts it from its normal position:
.nudge {
position: relative;
top: 10px;
left: 20px;
}That moves the element 10px down and 20px right. Read the offsets as "push away from": top: 10px pushes it away from its top edge, which moves it down.
The important part is what doesn't move. The space the element would have taken stays reserved, so nothing around it shifts to fill the gap. It can end up overlapping its neighbours.
On its own, that nudge is only occasionally useful. The real reason relative is everywhere is the next section. See Relative Positioning (opens in a new tab).
absolute: place it inside a parent
absolute takes the element out of the normal flow. The rest of the page acts as if it isn't there. Then top, right, bottom and left place it inside its containing block: the nearest ancestor whose position is anything other than static.
That is why you see these two together. Here is a "New" badge in the corner of a card:
<div class="card">
<span class="badge">New</span>
<h3>Cold brew</h3>
<p>Steeped for 18 hours.</p>
</div>.card {
position: relative; /* the badge's reference point */
padding: 24px;
}
.badge {
position: absolute;
top: 8px;
right: 8px;
}position: relative on the card doesn't move the card. It just makes the card the containing block, so top: 8px; right: 8px means "8px from the card's top-right corner".
Leave out position: relative on the card and the browser keeps looking up the tree for a positioned ancestor. If it finds none, the badge is placed against the page itself, and it jumps to the top-right of the whole page. That is the most common positioning bug there is. See Absolute Positioning (opens in a new tab).
To stretch an absolute element over its whole parent, as for an overlay, set all four offsets to 0. The inset shorthand does that in one line:
.overlay {
position: absolute;
inset: 0; /* same as top: 0; right: 0; bottom: 0; left: 0; */
}fixed: pin it to the window
fixed also takes the element out of the flow, but it positions it against the viewport, the visible browser window. When the page scrolls, the element stays put:
.back-to-top {
position: fixed;
bottom: 24px;
right: 24px;
}That button stays 24px from the bottom-right of the screen no matter how far down the reader scrolls.
Because a fixed element doesn't take up space, it can cover content. A fixed header, for example, usually needs matching padding-top on the page so the first lines aren't hidden behind it.
sticky: scroll, then stick
sticky is a mix of relative and fixed. The element scrolls with the page like normal until it reaches the offset you set, then it sticks there:
.section-title {
position: sticky;
top: 0;
background: white;
}As you scroll, each .section-title scrolls up until it touches the top of the window, then stays there while the rest of its section scrolls underneath. When its parent section scrolls out of view, the title goes with it. A sticky element only sticks within its parent.
The background matters. Without one, the content scrolling underneath shows through. See Fixed And Sticky (opens in a new tab).
Stacking with z-index
When positioned elements overlap, z-index decides which one is on top. Higher numbers sit in front:
.dropdown {
position: absolute;
z-index: 10;
}It only affects positioned elements, so if z-index seems to do nothing, check that the element has a position other than static. It is also relative to the element's stacking context, so a child can't climb above something its parent sits behind, however high its own number is.
Which one should I use?
- A badge, icon or label on a card:
absoluteon the badge,relativeon the card. - A dropdown menu under a button:
absoluteon the menu,relativeon the wrapper. - A header that stays visible:
stickywithtop: 0, orfixedif it should never scroll at all. - A chat or back-to-top button:
fixed. - Laying out a whole row or grid of items: none of these. Use Flexbox or Grid, which handle layout without pulling things out of the flow. What Is Flexbox (opens in a new tab) is the next step.
Common mistakes
- Forgetting
position: relativeon the parent. Anabsolutechild then positions itself against the page instead of its card. - Setting
toporlefton a static element. Nothing happens until you give it aposition. stickywithout an offset.position: stickydoes nothing until you addtop,bottom,leftorright.stickyinside a parent withoverflow: hiddenoroverflow: auto. That parent becomes the scrolling container, so the element sticks inside it, often meaning it never appears to stick at all. Check the ancestors foroverflow.- A
fixedelement that scrolls anyway. If an ancestor has atransform,filteror similar property, that ancestor becomes the reference point instead of the window. Move the fixed element outside it. - Using
absolutefor page layout. It works until the content changes size, then things overlap. Use Flexbox or Grid for layout, and save positioning for small, deliberate overlaps.
Quick recap
staticis the default and ignores offsets.relativenudges an element and makes it a reference point forabsolutechildren.absoluteleaves the flow and positions itself inside the nearest positioned ancestor.fixedpins an element to the browser window.stickyscrolls normally, then sticks at the offset you set, inside its parent.
Frequently asked questions
What is the default position in CSS?
static. Every element starts there, sitting in the normal flow of the page, and top, right, bottom and left have no effect on it.
What is the difference between relative and absolute?
A relative element stays in the flow and moves from its own normal spot, leaving its space behind. An absolute element leaves the flow entirely and is placed inside its nearest positioned ancestor.
What is the difference between fixed and sticky?
fixed is always pinned to the browser window and never scrolls. sticky scrolls with the page until it reaches its offset, then sticks, and only while its parent is on screen.
Why is my position sticky not working?
The usual causes are a missing top value, or an ancestor with overflow: hidden, auto or scroll. A parent that is no taller than the sticky element also leaves it nowhere to stick.
Why is my absolute element in the wrong place?
Its parent probably isn't positioned. Add position: relative to the element you want it placed inside.
Practice
The matching exercises are in the course. Start with Relative Positioning (opens in a new tab), place a badge in Absolute Positioning (opens in a new tab), then try both pinned values in Fixed And Sticky (opens in a new tab). Each exercise runs in your browser, so there is nothing to install.
Keep going
CSS Basics (opens in a new tab) takes you from your first rule through the box model, positioning, Flexbox and Grid. When you want to style faster, Tailwind CSS Basics (opens in a new tab) builds on the same ideas.







