Start.dev
Back to blog

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.

Ariel Weinberger
Ariel Weinberger ยท 7 min read

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

ValueStays in the normal flow?Positioned relative toTypical use
staticYesNothing, offsets are ignoredThe default for every element
relativeYes, its space is keptIts own normal positionSmall nudges, and a reference point for absolute children
absoluteNoThe nearest positioned ancestorBadges, icons, dropdowns, overlays
fixedNoThe browser window (viewport)Cookie bars, chat buttons, back-to-top buttons
stickyYes, until it sticksIts scrolling containerSection 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: absolute on the badge, relative on the card.
  • A dropdown menu under a button: absolute on the menu, relative on the wrapper.
  • A header that stays visible: sticky with top: 0, or fixed if 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: relative on the parent. An absolute child then positions itself against the page instead of its card.
  • Setting top or left on a static element. Nothing happens until you give it a position.
  • sticky without an offset. position: sticky does nothing until you add top, bottom, left or right.
  • sticky inside a parent with overflow: hidden or overflow: 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 for overflow.
  • A fixed element that scrolls anyway. If an ancestor has a transform, filter or similar property, that ancestor becomes the reference point instead of the window. Move the fixed element outside it.
  • Using absolute for 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

  • static is the default and ignores offsets.
  • relative nudges an element and makes it a reference point for absolute children.
  • absolute leaves the flow and positions itself inside the nearest positioned ancestor.
  • fixed pins an element to the browser window.
  • sticky scrolls 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.

Level up your coding skills

Jump into interactive courses and start building real-world projects.

Start learning

Related posts

Learn the CSS box model with clear diagrams and code: content, padding, border, and margin, plus box-sizing, margin collapse, and DevTools tips.
Ariel Weinberger
Ariel Weinberger ยท 8 min read
Should beginners learn Tailwind or plain CSS first? See the same card built both ways, what Tailwind assumes you know, and a learning order that works.
Ariel Weinberger
Ariel Weinberger ยท 5 min read
Flexbox or CSS Grid? Learn the one-dimensional vs two-dimensional rule, see both build the same layout, and get a quick way to pick the right one.
Ariel Weinberger
Ariel Weinberger ยท 6 min read