Position: static, relative, absolute, fixed, sticky
Containing blocks, stacking, sticky constraints, and when position is the right tool vs flex/grid.
- css
- position
- layout
- sticky
- absolute
position controls how an element participates in layout and what its offsets (top / right / bottom / left / logical insets) refer to. Most page structure should still be flex or grid. Position is for overlays, sticky chrome, badges, and deliberate removal from normal flow.
Docs: MDN position, MDN Understanding z-index, CSS Positioned Layout.
The five values
| Value | In normal flow? | Offsets relative to |
|---|---|---|
static (default) |
Yes | Offsets ignored |
relative |
Yes (space reserved) | Element’s normal position |
absolute |
No | Nearest positioned ancestor (or initial containing block) |
fixed |
No | Viewport (usually); transformed ancestors change this |
sticky |
Hybrid | Scrollport of nearest scroll ancestor, within containing block |
“Positioned” means position other than static. That definition matters for absolute containing blocks.
static and relative
.badge-host {
position: relative; /* establish containing block for absolute children */
}
.badge {
position: absolute;
top: 0;
right: 0;
transform: translate(40%, -40%);
}
relative without offsets looks like static but:
- Creates a containing block for absolute descendants.
- With offsets, visually moves the box while layout still uses the original space (gap left behind).
- Participates in stacking contexts when
z-indexis notauto.
Use relative on a host, not on every element “just in case.”
absolute
Removed from normal flow. Siblings pack as if it isn’t there. Size defaults can shrink-to-fit content unless you set dimensions or stretch with opposing insets (inset: 0 fills the containing block).
.modal-backdrop {
position: absolute;
inset: 0;
background: rgb(0 0 0 / 0.5);
}
Containing block for absolute: nearest ancestor with position ≠ static, or certain other cases (e.g. transform, filter, perspective, contain, will-change of those — they create containing blocks even when position: static). That last rule is a frequent “my fixed/absolute stuck to the wrong box” bug.
fixed
Stays put relative to the viewport while the page scrolls — typical for floating action buttons, cookie banners, or a global nav. Caveats:
- A transformed (or filtered, etc.) ancestor makes
fixedbehave more like absolute relative to that ancestor. - Mobile browser chrome and
100vhquirks still apply for full-viewport fixed UIs. - Fixed UI can obscure content and focus — reserve space or ensure focus management for dialogs (
dialog).
.toast-region {
position: fixed;
inset-block-end: 1rem;
inset-inline-end: 1rem;
z-index: 50;
}
sticky
Sticky is relative until a threshold, then stuck within its containing block like fixed — until the containing block scrolls away.
.table-wrap {
overflow: auto; /* sticky is relative to this scrollport */
max-height: 20rem;
}
th {
position: sticky;
top: 0;
background: var(--surface);
z-index: 1;
}
Requirements people miss:
- A scroll ancestor (overflow not
visibleon an ancestor, or the viewport). - An inset (
top,bottom, …) — without it, sticky has nothing to stick to. - Room to stick: if the sticky element’s parent is exactly as tall as the element, it never “travels.” Parent must be taller (e.g. sticky
thinside a talltable, or sticky sidebar inside a long column). overflow: hiddenon an ancestor often kills sticky for descendants.- Parent backgrounds: when stuck, content scrolling underneath needs an opaque sticky background or content shows through.
Sticky headers for whole pages usually use top: 0 on a header inside document flow — simpler than fixed + spacer if the header’s natural height is fine.
Stacking and z-index
z-index only applies to positioned elements (and flex/grid items). It works within a stacking context. New contexts appear from positioned + z-index, opacity < 1, transform, filter, and more. A child with z-index: 9999 cannot escape a parent context that sits under a sibling.
/* Both create contexts; painting order is between .a and .b first */
.a { position: relative; z-index: 1; }
.b { position: relative; z-index: 2; }
.a .popup { z-index: 9999; } /* still under .b’s tree if .b is above .a */
Debug stacking by finding which ancestor created the context, not by inflating magic numbers. See also stacking contexts.
Position vs layout systems
| Problem | Prefer |
|---|---|
| Nav with logo left, actions right | Flex |
| Page columns and regions | Grid |
| Badge on avatar corner | Absolute + relative host |
| Header that stays while body scrolls | Sticky (or fixed + padding) |
| Full-screen modal | Prefer native dialog / top layer; absolute/fixed as fallback |
Absolute for entire page layouts is how we got unmaintainable CSS in 2012. Don’t go back.
Performance note
Animating top / left invalidates layout more often than animating transform. For motion, prefer compositor-friendly properties — same story as the rendering pipeline and Core Web Vitals (INP/jank). Sticky/fixed chrome can also contribute to CLS if it appears late without reserved space.
Interview angle
Define each of the five values in one line. Explain absolute containing blocks and the transform/fixed gotcha. Explain sticky’s need for top + scroll ancestor + tall enough parent. Mention stacking contexts so z-index: 9999 isn’t your only strategy.
Live coding: avatar with notification badge; sticky table header inside an overflow container; prove sticky breaks when parent is overflow: hidden.
Related on this site
- CSS Box Model
- Flexbox fundamentals
- CSS Grid fundamentals
- Stacking contexts and z-index
- Sticky headers and sidebars
- Browser rendering pipeline
- dialog element
Further reading
Related guides
- Sticky Headers and Sidebarsposition: sticky for headers and side rails — containing blocks, overflow traps, top offsets, and stacking with page chrome.
- CSS Box ModelContent, padding, border, margin, box-sizing, and how the math actually works for layout and overflow.
- CSS Grid FundamentalsTwo-dimensional layout with tracks, gaps, placement, fr units, and when grid beats flex for page scaffolding.
- CSS Grid vs Flexbox When to UseOne-dimensional flex vs two-dimensional grid — decision rules for navs, card grids, page shells, and nested layouts.
- Display Block Inline Flex GridHow display changes layout participation — block vs inline vs inline-block, and when flex/grid formatting contexts take over.