11: Position & Stacking

How to put things on top of other things without regret. Plus the only z-index lesson you'll need.

The five positions

positionLeaves flow?Offset with top/left…?Use
staticNo (default)NoNormal flow
relativeNo (stays in flow)Yes, nudges visuallyContaining block for absolute
absoluteYes (removed)Yes, against nearest positioned ancestorTooltips, modals
fixedYesYes, against viewportFixed header, overlay
stickyNo until scrollSticky when parent scrollsSticky headers, TOC

Relative: the anchor

relative, nudged 12px/6px but original space kept

This text doesn't move. Relative keeps its flow space.

.card { position: relative; top: 6px; left: 12px; } /* visual nudge only */

Absolute: positioned against nearest non-static ancestor

relative parent (containing block) absolute top:8 right:8

Without position: relative on parent, the badge would pin to viewport.

.parent { position: relative; } /* creates containing block */
.badge  { position: absolute; top: 8px; right: 8px; }

Fixed: glued to viewport

Fixed would stay while you scroll. I can't demo that in a box, but here's the code:

.topbar { position: fixed; top: 0; left: 0; right: 0; height: 48px; z-index: 100; }

Sticky: the best of both

Sticky header. Scroll inside this box

Line 1, keep scrolling

Line 2

Line 3

Line 4, header sticks to top

Line 5

Line 6

Line 7

Line 8, header stays until parent ends

.toc { position: sticky; top: 0; } /* sticks when you would scroll past it */
Gotcha: sticky needs a scroll container and won't work if any ancestor has overflow: hidden.

z-index & stacking context

z-index only works on positioned elements (relative/absolute/fixed/sticky). Higher number means on top, at least within the same stacking context.

z:1
z:2 on top
z:0 bottom
.a { position: absolute; z-index: 1; }
.b { position: absolute; z-index: 2; } /* on top within same parent */
Gotcha: New stacking contexts are created by position + z-index, but also opacity < 1, transform, filter. A child with z-index: 9999 can't escape its parent's stacking context. If parent is z-index: 1, that 9999 is only "9999 within 1". Keep z-index flat: e.g., 10, 20, 30 for layers.

Use cases

Tooltip

Hover me Tooltip! absolute + transform centering (Module 7 again!)
.wrap { position: relative; display: inline-block; }
.tip  { position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); }

Modal (uses fixed + inset + margin auto from Module 7)

Modal centered card
In a real page: position: fixed; inset: 0; margin: auto
.overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.4); display: grid; place-items: center; }
.modal   { width: 400px; max-width: 90vw; background: #fff; }

Float, just so you recognize it

float: left was the old layout hack before flex/grid. You'll see it in legacy code. It wraps text around images and needs clearfix hacks. Don't use it for layout. Use flex/grid.

Exercise: Modal + sticky header (20 min):
  1. Build a page with a sticky header (position: sticky; top: 0).
  2. Add a button that shows a modal: overlay (fixed; inset: 0) + card (margin: auto or place-items: center).
  3. Give overlay z-index: 10, modal z-index: 11. Try breaking by wrapping modal inside a low z-index parent and see what happens to stacking context.
Prev10: Grid Next12: Responsive