The CSS position property controls how an element is placed in the document — whether it flows normally with the rest of the page or gets pulled out and placed relative to something else entirely. It's one of the most misunderstood properties in CSS, mostly because its behavior changes depending on which value you use and what its parent elements are doing.

Understanding position properly unlocks a huge range of common UI patterns: dropdown menus, modals, sticky headers, badges pinned to the corner of a card, and more. Get it wrong and elements end up overlapping, disappearing, or refusing to move where you expect.

The Five Position Values

static is the default — the element just follows normal document flow, and top/right/bottom/left have no effect on it at all.

relative also follows normal flow, but now top/left/etc. will nudge it visually from where it would otherwise sit — without affecting the layout of surrounding elements. Its original space in the flow is preserved, like a ghost outline.

absolute removes the element from normal flow completely and positions it relative to its nearest positioned ancestor (any ancestor that isn't static) — or relative to the whole page if no such ancestor exists.

fixed works like absolute, but positions relative to the browser viewport itself, so it stays in place even as the page scrolls.

sticky is a hybrid: the element behaves like relative until the page scrolls past a threshold you set, at which point it "sticks" in place like fixed, relative to its scrolling container.

The Classic Pattern: Absolute Inside Relative

By far the most common real-world use of positioning is placing a small badge or icon in the corner of a card. You do this by making the card relative (so it becomes the positioning anchor) and the badge absolute (so it positions relative to that card, not the whole page).

<div class="card">
  <span class="badge">New</span>
  <h3>Product Title</h3>
  <p>Product description goes here.</p>
</div>
.card {
  position: relative;
  width: 240px;
  padding: 16px;
  border: 1px solid #ddd;
  border-radius: 8px;
}

.badge {
  position: absolute;
  top: -10px;
  right: -10px;
  background: #e11d48;
  color: white;
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 999px;
}

This renders a card with a small red "New" pill floating slightly outside its top-right corner. Because .card is relative, the badge's top/right values are measured from the card's edges — not the page's. Remove position: relative from .card and the badge would jump to the corner of the entire viewport instead.

Sticky Headers and Sidebars

position: sticky needs a threshold — usually top: 0 — to know when to "activate." It only sticks within the bounds of its parent container; once you scroll past the parent, it scrolls away with it.

.site-header {
  position: sticky;
  top: 0;
  background: white;
  box-shadow: 0 1px 4px rgba(0,0,0,0.1);
  z-index: 10;
}

This keeps the header pinned to the top of the viewport once the page scrolls past it, giving users persistent access to navigation without writing any JavaScript. Notice the z-index — sticky and fixed elements almost always need one to make sure they sit above the content scrolling underneath them, a topic covered in more depth in z-index and stacking contexts.

A quick mental model that helps: relative moves an element from where it would have been; absolute and fixed remove it from flow and place it relative to an ancestor or the viewport; sticky is relative until a scroll threshold, then acts like fixed within its parent's bounds.