Skip to content

Positioning — Breaking Out of Flow

Flexbox and Grid arrange elements in a predictable flow. But sometimes you need an element that breaks out of that flow — a notification badge floating over an icon, a sticky header that stays visible while the page scrolls, a tooltip that hovers above content. That is where CSS positioning comes in.

Every element has a position property. By default it is static, which means it just lives in normal flow. The moment you change it to anything else, you gain superpowers — and a few new responsibilities.

position: static — The default. Elements sit in normal document flow. top, left, right, bottom, and z-index have no effect.

position: relative — The element stays in the normal flow, but you can now nudge it with top/left/right/bottom. More importantly, it creates a positioning context — any absolute children will be positioned relative to this element, not the whole page. Think of it as “I want this element to be the origin point for my badge.”

position: absolute — The element is removed from normal flow entirely. It is placed relative to the nearest ancestor that has a non-static position. Other elements in the flow do not see it — they act as if it is not there. Perfect for badges, tooltips, dropdowns, and overlays.

position: sticky — A hybrid. The element sits in normal flow until you scroll past a threshold, then it “sticks” to the viewport edge (defined by top, right, etc.) until its parent scrolls out of view. Perfect for navigation bars and table headers.

When positioned elements overlap, z-index controls which one appears on top. Higher numbers are on top. It only works on elements that have a non-static position.

Think of it exactly like the Figma layer order panel — higher in the stack = on top — except in CSS you express it as a number instead of dragging.

Things to try:

  • Change .badge top: -8px and right: -8px to different values — move the badge around
  • Change .badge position: absolute to position: relative — watch it fall back into normal flow
  • Remove position: relative from .card — the badge will now position relative to the whole document
  • Change .sticky-bar from position: sticky to position: relative — it stops sticking when you scroll
Which position value removes an element from the normal document flow?
You want a badge to float over the top-right corner of a card. What should the card have?
What does position: sticky do?
z-index only works on elements that have: