Course outline · 0% complete

0/30 lessons0%

Course overview →

position: pinning and layering

lesson 5-4 · ~10 min · 17/30

Leaving the flow

So far every box has sat in normal flow, where blocks stack, inlines line up, and each element's place depends on the ones before it.

Real interfaces are full of elements that must not flow, such as a header that stays put while the page scrolls, a notification badge sitting on the corner of an icon, or a dropdown floating over the content below it. The position property exists for exactly these cases.

.badge { position: absolute; top: -8px; right: -8px; }
ValueIn flowPlaced against
staticyesnothing, offsets are ignored
relativeyesits own normal spot
absolutenothe nearest positioned ancestor
fixednothe viewport
stickyyes, until it pinsthe viewport, past the offset

static is the default. relative keeps the element in flow while allowing a nudge, and the important part is that it becomes an anchor for absolute children.

A positioned ancestor is the closest ancestor whose position is anything but static. The absolute-anchoring rule is the one that bites, because forgetting to position the parent sends a corner badge to the corner of the whole page.

The two recipes worth memorizing

A badge on a corner is the pattern behind every cart count and unread dot. Position the parent relative, which changes nothing visually, then place the child absolutely against it.

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

A header that stays while the page scrolls needs two declarations.

.site-header {
  position: sticky;
  top: 0;
}

When positioned elements overlap, z-index decides which one paints on top. Higher numbers win, and it only works on positioned elements.

LayerSuggested z-index
ordinary content1
raised elements10
overlays100

Keeping the values few and small is the discipline here, since z-index arms races reaching 99999 are a real codebase smell and a sign that nobody knows which layer anything belongs to.

no positioned ancestorparent is position: relativebuttonbuttonthe badge anchors to the pagethe badge anchors to the button99
An absolute child is placed against its nearest positioned ancestor, so one declaration on the parent decides whether a badge lands on the button or in the corner of the page.

Absolute placement with no anchor

A .badge at position: absolute whose ancestors set no position is placed against the page itself.

Absolute placement searches upward for the nearest ancestor whose position is not static. With no positioned ancestor it anchors to the page.

Parent positionBadge anchors to
none setthe page
relativethe parent
absolutethe parent

That is why the fix is one line on the parent, namely position: relative. The symptom is unmistakable once recognized, since the badge appears in the top corner of the document rather than beside the thing it belongs to.

A badge pinned to a button corner

The standard two-part positioning recipe, with an anchor and an absolutely placed child.

CSS

.bell {
  position: relative;
  font-size: 1rem;
  padding: 12px 20px;
  margin: 24px;
  border: 1px solid gray;
  border-radius: 8px;
  background: white;
}

.badge {
  position: absolute;
  top: -8px;
  right: -8px;
  background: #d4af37;
  border-radius: 999px;
  padding: 2px 8px;
  font-size: 0.8rem;
}
ElementDeclarationRole
.bellposition: relativebecomes the anchor
.badgeposition: absoluteleaves the flow
.badgetop: -8px; right: -8pxstraddles the corner

The parent needs position: relative so the badge anchors to it rather than the page, and negative offsets pull the badge just past the parent's edge.

border-radius: 999px is a common idiom for a fully rounded pill, since any radius larger than half the height produces the same capsule shape.

Pinning to the viewport

The value that pins an element to the viewport so it does not move when the page scrolls is position: fixed.

The element leaves normal flow and is placed against the viewport itself, so scrolling the page does not move it.

ValueScrolls firstThen
fixedneveralways pinned
stickyyespins at the offset

sticky is the softer cousin, scrolling normally until it reaches the given offset and pinning from there. Because a fixed element is out of flow, the content behind it needs padding or margin to avoid sitting underneath.