positioning

[check: …]
📖 Pair this live demo with the companion guide (.md) — this page is the rendered ground truth.

The 5 position values — side by side

The stage is position:relative (a positioned ancestor) with overflow:auto — so it is BOTH the anchor for the absolute box AND the scroll container for the sticky box. Scroll inside the stage to watch sticky pin, and scroll the page to watch the fixed chip stay put.

Scroll me ↓ — overflow:auto makes this box a scroll container.

relativeleft:24 · in flow, space kept
statictop/left IGNORED

More content to make the stage scrollable. A stickily-positioned element flows normally until it crosses its top threshold, then it sticks to the nearest scrolling ancestor.

An absolutely-positioned box is taken OUT of normal flow — no space is reserved. It anchors to its nearest positioned ancestor (this stage), at the offset given by top/left.

Keep scrolling ↓ — the gold sticky box above should stay pinned at top:6px while the rest of this text moves under it.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation.

absolute · top:12 left:12→ positioned ancestor

Trailing filler so the absolute box (bottom) and sticky box both have room to demonstrate their behaviour.

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident.

then scroll the page — it never leaves the screen
valuecomputedoffsetLeftoffsetParent
static
relative
absolute
sticky
fixed

offsetParent = the nearest positioned ancestor (the containing block for positioned boxes). For fixed it is null (the containing block is the viewport, not an element).

The stacking context — why z-index "doesn't reach outside"

Two positioned parents overlap. The indigo childA has a huge z-index:999. By default its z-index competes at the root and it wins. But the moment its parent (parentA) gets transform:translateZ(0), parentA becomes a stacking context and traps childA inside it — so the whole parentA unit (z-index auto ≈ 0) loses to parentB (z-index 1). Toggle it and watch elementFromPoint() flip which box is on top at the overlap.

parentAno transform
childA · z-index:999
parentB · z-index:1

document.elementFromPoint(x, y) returns the topmost element at a pixel — the live paint order. childA(999) on top ⇒ no parent context; parentB(1) on top ⇒ parentA trapped childA.

fixed
pinned to the VIEWPORT
(scroll the page)