Tips・Cheat sheets

The position quick reference

The reference that answers "it flew somewhere odd when I set absolute" and "sticky isn't working." The five values, how the reference point is decided, and the knack of z-index—with a live demo.

position is the property in charge of stacking, floating, and pinning—yet everyone has had an element vanish off-screen the instant they set absolute. This article is a quick reference to the five values and how the reference point gets decided.

CSSLayer and pin things with positionLearning position for the first time? Start with this lesson of the CSS course. This article is for remembering

The five values at a glance

ValueIts original spaceReference pointMainly used for
staticKeptCan’t be offset (default)Cancelling another value
relativeKeptIts own original positionNudging / becoming a reference for absolute
absoluteRemovedNearest ancestor that isn’t staticBadges, text over images, a button top-right
fixedRemovedThe viewportSticky headers, a floating button
stickyKeptSwitches as you scrollPinned headings, table headers

The most important column is “is its original space kept, or removed?” When it’s removed (absolute, fixed), the space it occupied closes up and everything around it moves.

Switch values in the demo below to see the difference. The orange box is the one with position on it.

With relative, box 3 stays put; with absolute, box 3 slides left. That’s what “taken out of the flow” means.

Positioning with top / right / bottom / left

Setting position to anything but static unlocks these four.

.badge {
  position: absolute;
  top: 8px;      /* 8px down from the reference's top */
  right: 8px;    /* 8px in from the reference's right */
}

top: 8px means “8px down from the top edge”; right: 8px means “8px left from the right edge.” Remember it as inward from the side you name.

There’s also inset, which writes all four at once.

.overlay {
  position: absolute;
  inset: 0;       /* top, right, bottom, left all 0 = fills the parent */
}

inset: 0 is the shortest way to say “cover the parent exactly.” It’s the go-to for laying a translucent black over a photo.

How absolute decides its reference point

This is where position trips up the most people. An absolute element walks up its ancestors and uses the first one whose position isn’t static.

<div class="card">          <!-- position: relative -->
  <img src="photo.jpg" alt="A photo">
  <span class="badge">NEW</span>   <!-- position: absolute -->
</div>
.card {
  position: relative;    /* ← this declares the reference */
}
.badge {
  position: absolute;
  top: 8px;
  right: 8px;
}

Without position: relative on .card, the badge flies to the top right of the whole page. That’s the cause of “it went somewhere odd” essentially every time.

fixed—pin it to the screen

The reference becomes the screen itself, so it doesn’t move as you scroll.

.header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
}

sticky—pin it partway

sticky is the halfway behaviour: it flows normally until scrolling brings it to the offset you named, then it pins.

.section-title {
  position: sticky;
  top: 0;
}

The difference from fixed is that it never leaves its parent. Once the parent scrolls past, it scrolls away with it. That’s how you get “each section’s heading pins, and swaps at the next section” in one line.

z-index—the stacking order

Which element wins when things overlap is decided by z-index. Bigger numbers come forward.

.modal {
  position: fixed;
  z-index: 100;
}

One prerequisite matters: z-index only works on elements whose position isn’t static. If it “does nothing,” set position first.

The other trap is stacking contexts. If an ancestor has z-index, transform, or opacity below 1, everything inside it only competes within that layer. z-index: 9999 on a child won’t help if the whole layer sits behind. When something “just won’t come forward,” walk up the ancestors.

Six recipes you can use as-is

A badge at the top right of an image

.card {
  position: relative;
}
.badge {
  position: absolute;
  top: 8px;
  right: 8px;
}

A translucent black over a photo

.thumb {
  position: relative;
}
.thumb::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.35);
}

Centre an overlaid element exactly

.parent {
  position: relative;
}
.child {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);   /* pull back half its own size */
}

top: 50% alone puts the element’s top-left corner at the centre. Pulling back half its own size with transform is the other half of the pair.

A header that survives scrolling

.header {
  position: sticky;
  top: 0;
  z-index: 10;
}

Using sticky instead of fixed means the content never slips underneath, so you don’t have to compensate with padding. For a pinned header, sticky is the easier choice.

A floating button at the bottom right

.fab {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 50;
}

Freeze a table’s header row

thead th {
  position: sticky;
  top: 0;
  background: #fff;   /* a background is required, or the rows show through */
}

Checklist when it isn’t working

  • top / left do nothingposition is still static. Set relative or another value first
  • absolute flies far away → add position: relative to the parent you meant as the reference
  • sticky won’t move → did you write top? does an ancestor have overflow? is the parent tall enough?
  • z-index does nothing → did you set position? is an ancestor creating a stacking context?
  • fixed won’t pin to the screen → a transform or filter on an ancestor makes that ancestor the reference instead
  • A horizontal scrollbar appeared → an absolute or fixed element is sticking out past the screen. Revisit the right value

Summary

  1. The key difference among the five values is whether the original space is kept or removed
  2. absolute measures from the nearest ancestor that isn’t static—writing relative on the parent is the other half of the pair
  3. sticky only pins within its parent. An offset is required; watch ancestors’ overflow
  4. z-index only works on elements with position set
  5. For a pinned header, sticky beats fixed (nothing slips underneath)
TipsCopy-paste CSS layout collectionFor centring and overlay examples, see the copy-paste layout collection

position stops being scary the moment you can say where the reference point is. If something flies away, look at the parent first.

FAQ

My element flew somewhere unexpected when I set absolute
An absolute element is positioned against the nearest ancestor whose position is something other than static. If no such ancestor exists, the whole page becomes the reference, which sends it far away. Adding position: relative to the parent you meant as the reference fixes it.
position: sticky isn't working
Check three things. (1) Have you written a position offset such as top or bottom? Sticky requires one. (2) Does an ancestor have overflow: hidden or overflow: auto? Either kills it. (3) Is the parent tall enough? Sticky can only stick within its parent.
What's the difference between relative and absolute?
relative shifts the element while keeping the space it occupied; absolute takes it out of the flow and places it by coordinates from its reference point. relative still affects the layout around it; absolute doesn't.
z-index doesn't bring my element forward
z-index only affects elements whose position is something other than static, so set position first. If it still won't come forward, an ancestor may be creating a stacking context with z-index, transform, or opacity, and that whole layer is sitting behind.