Positioning and stacking

Introduction

Most of a page should stay in normal flow, because that is what makes it adapt to any screen. But some things need to step out: a notification badge on a button, a header that stays visible, a dialog which covers everything. That is the job of the position property, and used sparingly it is very powerful.

How it works

ValueIn the flow?Offsets measured from
static (default)yesoffsets do nothing
relativeyes, and its space is keptits own normal position
absolutenonearest positioned ancestor
fixednothe viewport
stickyyesscrolls until it reaches its threshold

The offsets are top, right, bottom and left. The shorthand inset: 0 sets all four at once.

Relative and absolute together. This is the pattern you will use most. An absolute element measures its offsets from its nearest positioned ancestor. So you put position: relative on the parent, even without offsets, and the child can be placed against the parent's corners:

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

If no ancestor is positioned, the badge is placed against the initial containing block, and it can land in a surprising corner of the page.

Absolute elements leave the flow, so everything else closes up as if they were not there, and they can overlap content. Use them for decoration, not for main layout.

Fixed. A fixed element is placed against the viewport and stays put during scrolling. Note that a transform on an ancestor changes this, making it behave like absolute in relation to that ancestor.

Sticky. A sticky element scrolls like normal, then sticks when it reaches a threshold such as top: 0. It stays inside its parent and stops sticking at the parent's end. It needs a threshold, and an ancestor with overflow: hidden or auto often stops it from working as expected.

Layers. Elements that overlap are normally painted in source order. z-index changes this for positioned elements and for flex and grid items. A higher number goes on top.

Why does z-index seem not to work? Some properties, including opacity below 1, transform and a positioned element with a z-index, create a stacking context. Think of it as a sealed folder. Everything inside is stacked together and the folder takes one place in the outer pile. A child with z-index: 9999 cannot rise above something outside its folder that is higher than the folder itself.

Syntax

.overlay {
  position: fixed;
  inset: 0;
  background: rgb(0 0 0 / 0.5);
}

Example

console.log output from the page shows here.

Line by line

  1. position: relative on the card makes it the reference point.
  2. position: absolute removes the badge from the flow.
  3. top and right place the badge 6 pixels from the card's top right corner.

Expected result

A pale box reading "Messages" with a red round-ended badge reading 3, in its top right corner.

In the real world

Dropdown menus, tooltips, badges and dialogs use absolute or fixed positioning. Sticky table headers and section titles are common too. Teams frequently keep a short list of named z-index levels, to prevent a contest of ever larger numbers.

Common mistakes

  • Forgetting position: relative on the parent.
  • Using absolute positioning for page layout, which breaks on other screen sizes.
  • Setting sticky without a threshold.
  • Raising z-index higher and higher without checking the stacking context.

Best practices

  • Stay in normal flow unless there is a clear reason not to.
  • Pair relative on a parent with absolute on a child.
  • Keep z-index values small and deliberate.
  • Make sure fixed elements do not hide focused content.

Summary

relative shifts an element while keeping its space. absolute removes it and places it against a positioned ancestor. fixed attaches it to the viewport, and sticky switches between the two behaviours. z-index only works inside a stacking context.

Quick check

1. An absolute element is positioned against:
2. What does a sticky element need in order to stick?
3. A child has z-index: 9999 but appears below something. A likely reason is:

Key takeaways

  • relative keeps an element's space; absolute removes it from the flow.
  • An absolute element is placed against its nearest positioned ancestor.
  • sticky needs a threshold, such as top: 0.
  • z-index only compares elements within the same stacking context.

Practice problems for this lesson

My notes

Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.