Positioning & Stacking Context
Checking access...
Positioning lets you remove elements from the normal document flow and place them exactly where you want. It’s essential for modals, dropdowns, tooltips, sticky headers, and overlays.
The Position Property
.element { position: static; /* Default — normal document flow */ position: relative; /* Offset from normal position */ position: absolute; /* Removed from flow, positioned relative to ancestor */ position: fixed; /* Removed from flow, positioned relative to viewport */ position: sticky; /* Hybrid: normal flow until scroll threshold, then fixed */}Position Values
Static (Default)
.element { position: static; /* top, right, bottom, left, z-index have NO effect */}Elements flow in normal document order. You cannot offset them with top/left.
Relative
.element { position: relative; top: 10px; left: 20px;}- Element stays in normal flow (occupies original space)
- Offset is calculated from its original position
- Other elements are NOT affected (they act as if the element is still in its original spot)
- Creates a new positioning context for child
absoluteelements
Absolute
.child { position: absolute; top: 0; left: 0;}- Element is REMOVED from normal flow (takes no space)
- Positioned relative to the nearest positioned ancestor (any ancestor with
position: relative | absolute | fixed | sticky) - If no positioned ancestor, positioned relative to
<html>(the initial containing block) - Other elements act as if it doesn’t exist
The parent-child pattern is essential:
.parent { position: relative; /* Creates reference point for child */ /* width, height, etc. */}
.child { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); /* Perfect centering */}Fixed
.fixed-header { position: fixed; top: 0; left: 0; width: 100%; z-index: 100;}- Removed from normal flow
- Positioned relative to the viewport (browser window), not any ancestor
- Stays in place when the page scrolls
- Common uses: sticky headers, back-to-top buttons, cookie consent banners
Sticky
.sticky-nav { position: sticky; top: 0; /* Becomes fixed when scroll reaches this point */ z-index: 10;}- Behaves like
relativenormally (in flow, takes space) - Becomes
fixedwhen scroll reaches the specified threshold - Only works within its parent container — scrolls away when the parent exits viewport
- Requires at least one of:
top,right,bottom, orleft
Offsetting with top/right/bottom/left
/* These only work on positioned elements (not static) */.absolute-element { position: absolute; top: 10px; /* 10px from top edge of positioned ancestor */ right: 20px; /* 20px from right edge */ bottom: 30px; /* 30px from bottom edge */ left: 40px; /* 40px from left edge */}
/* Stretch to fill */.stretched { position: absolute; top: 0; right: 0; bottom: 0; left: 0; /* This element fills its positioned ancestor completely */}
/* Center an absolutely positioned element */.centered { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);}Z-Index and Stacking Context
When positioned elements overlap, z-index controls which appears on top:
.element1 { position: absolute; z-index: 1; /* Lower number = behind */}
.element2 { position: absolute; z-index: 2; /* Higher number = in front */}Z-Index Rules
- Only works on positioned elements (not
static) - Higher values appear in front of lower values
- Can be negative (behind normal flow elements)
- Default is
auto(same as0) - Each stacking context is isolated from others
What Creates a New Stacking Context
A stacking context is a group of elements whose z-index is scoped. New contexts are created by:
/* Position + z-index (most common) */.element { position: relative; z-index: 1;}
/* Opacity less than 1 */.element { opacity: 0.9; }
/* Transform */.element { transform: scale(1); }
/* Filter */.element { filter: blur(2px); }
/* Will-change */.element { will-change: transform; }
/* Container queries */.container { container-type: inline-size; }
/* Isolation (explicitly creates context) */.element { isolation: isolate; }Important: A child’s z-index is scoped to its stacking context. If parent A has z-index: 1 and parent B has z-index: 2, children of A will always be behind children of B, regardless of their individual z-index values.
Practical Patterns
1. Modal Overlay
.overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; z-index: 1000;}
.modal { background: white; padding: 24px; border-radius: 8px; max-width: 500px; width: 90%;}2. Badge on a Card
.card { position: relative; /* Anchor for badge */ padding: 20px; border: 1px solid #ddd;}
.badge { position: absolute; top: -8px; right: -8px; background: red; color: white; padding: 4px 8px; border-radius: 4px; font-size: 12px;}3. Fixed Back-to-Top Button
.back-to-top { position: fixed; bottom: 24px; right: 24px; width: 48px; height: 48px; background: #0066cc; color: white; border: none; border-radius: 50%; cursor: pointer; z-index: 100;}4. Dropdown Menu
.dropdown { position: relative; /* Anchor for menu */}
.dropdown-menu { position: absolute; top: 100%; /* Below the trigger */ left: 0; background: white; border: 1px solid #ddd; border-radius: 4px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); display: none; /* Hidden by default */ min-width: 200px; z-index: 10;}
.dropdown:hover .dropdown-menu { display: block; /* Show on hover */}5. Sticky Section Headers
.section-header { position: sticky; top: 0; background: white; padding: 12px 0; z-index: 5; border-bottom: 2px solid #333;}Try It Yourself
Create a page with:
- A fixed header at the top (stays on scroll)
- Card components with “New” badges positioned at top-right
- A modal overlay (use a fixed full-screen background with centered modal)
- A dropdown menu triggered by hover
- A sticky sidebar that follows as you scroll
- A back-to-top button fixed at bottom-right
- Experiment with z-index to understand stacking
Key Takeaways
position: relativeoffsets from normal position and anchors absolute childrenposition: absoluteremoves from flow, positions relative to nearest positioned ancestorposition: fixedremoves from flow, positions relative to viewport (stays on scroll)position: stickyis relative until scroll threshold, then fixedz-indexonly works on positioned elements (not static)- Each stacking context isolates its children’s z-index from other contexts
- The parent-child
position: relative+position: absolutepattern is fundamental - Use
transform: translate(-50%, -50%)for perfect centering of absolute elements