Chapter 24: Positioning & Z-Index
Positioning + z-index
These two properties together let you take elements out of the normal document flow and place them exactly where you want, even stacking them on top of each other like layers in Photoshop.
But they are also responsible for 90% of “why is this element hiding behind something?”, “why is my fixed navbar broken?”, “why is this modal not centered?” frustrations.
So let’s go very slowly, very clearly, with lots of visuals in words and a real, playable example.
1. The Five Positioning Values (What They Actually Do)
| Value | Takes element out of normal flow? | Respects document flow of others? | Reference point for top/right/bottom/left | Most common real-world uses |
|---|---|---|---|---|
| static | No | Yes | — (ignored) | Default — almost everything starts here |
| relative | No | Yes | Its original position | Small nudges, creating space for absolute children |
| absolute | Yes | No | Nearest positioned ancestor (or body) | Overlays, tooltips, badges, modals, dropdowns |
| fixed | Yes | No | Viewport (browser window) | Sticky headers, floating action buttons, chat widgets |
| sticky | No (until scroll threshold) | Yes (until stuck) | Its flow position until it sticks | Sticky table headers, section titles, sidebars |
Key sentence to memorize:
Only elements with position ≠ static respond to top, right, bottom, left and create a new stacking context for their absolute/fixed children.
2. z-index – Who Sits on Top?
z-index only works on elements that have:
- position: relative | absolute | fixed | sticky
- (or position: static with some rare transform/filter tricks — ignore for now)
Higher z-index = sits on top Default z-index = auto (usually behaves like 0)
Stacking order rules (when z-index is equal):
- Later in HTML = on top
- Stacking contexts (more on this later)
3. Real, Hands-On Example – Play with This Right Now
index.html
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Positioning & z-index Explained – Webliance</title> <link rel="stylesheet" href="style.css"> </head> <body> <div class="playground"> <h1>Positioning Playground</h1> <!-- Normal flow boxes (static) --> <div class="box static">Static (default)</div> <div class="box static">Another static</div> <!-- Relative – shifted but keeps space --> <div class="box relative"> Relative – moved 40px down & 60px right <div class="absolute-child">Absolute child inside relative</div> </div> <!-- Absolute – taken out of flow --> <div class="box absolute-main"> Absolute – positioned relative to nearest positioned ancestor (or viewport) </div> <!-- Fixed – stays in viewport --> <div class="fixed-box">Fixed – always here (scroll me ↓)</div> <!-- Sticky – sticks when you scroll past it --> <div class="sticky-header">Sticky Header – scrolls normally until top, then sticks</div> <div class="long-content"> <h2>Scroll down to see sticky & fixed in action</h2> <p>Keep scrolling...</p> <p>(add more content if needed)</p> <!-- Imagine 20 more paragraphs here --> <p style="height: 150vh; background: linear-gradient(#f8fafc, #e0f2fe);">Scroll area</p> </div> <!-- z-index stacking demo --> <div class="stacking"> <div class="stack-box red">z-index: 10</div> <div class="stack-box blue">z-index: 5</div> <div class="stack-box green">z-index: 1</div> <div class="stack-box yellow">No z-index (auto = 0)</div> </div> </div> </body> </html> |
style.css
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
/* Reset */ * { margin:0; padding:0; box-sizing:border-box; } body { font-family: system-ui, sans-serif; background: #f8fafc; color: #1e293b; line-height: 1.6; min-height: 200vh; /* so we can scroll */ } .playground { max-width: 1000px; margin: 0 auto; padding: 40px 20px; } h1 { text-align: center; color: #1d4ed8; margin-bottom: 3rem; } /* Base box style */ .box { width: 300px; height: 140px; background: white; border: 4px solid; border-radius: 12px; padding: 20px; margin: 20px auto; text-align: center; font-weight: bold; position: relative; /* so we can position children */ } /* ── Positioning examples ── */ .static { border-color: #9ca3af; } .relative { border-color: #3b82f6; top: 40px; left: 60px; } .absolute-main { position: absolute; top: 180px; right: 100px; border-color: #ef4444; z-index: 2; } .absolute-child { position: absolute; top: 20px; right: -40px; background: #10b981; color: white; padding: 10px 20px; border-radius: 8px; font-size: 0.9rem; white-space: nowrap; } .fixed-box { position: fixed; bottom: 30px; right: 30px; background: #8b5cf6; color: white; padding: 16px 24px; border-radius: 50px; box-shadow: 0 6px 20px rgba(139,92,246,0.3); z-index: 1000; } .sticky-header { position: sticky; top: 0; background: #1e40af; color: white; padding: 1.5rem; text-align: center; font-size: 1.4rem; z-index: 500; margin: 4rem 0 2rem; } /* Stacking demo */ .stacking { position: relative; height: 300px; margin: 6rem 0; border: 2px dashed #94a3b8; border-radius: 12px; } .stack-box { width: 180px; height: 180px; position: absolute; border-radius: 12px; color: white; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 1.3rem; } .red { background: #ef4444; top: 40px; left: 40px; z-index: 10; } .blue { background: #3b82f6; top: 80px; left: 80px; z-index: 5; } .green { background: #10b981; top: 120px; left: 120px; z-index: 1; } .yellow { background: #fbbf24; top: 160px; left: 160px; /* no z-index */ } |
What You Should Do Right Now
- Open the file with Live Server
- Scroll down → watch sticky header stick at top
- See fixed purple button always stay bottom-right
- Notice how absolute child stays relative to its blue relative parent
- Look at stacking squares → higher z-index sits on top
- Try removing position: relative from .relative → absolute child jumps to body
Quick Mastery Checklist – Positioning & z-index
- static = normal flow (default)
- relative = nudge + stacking context for children
- absolute = taken out of flow, positioned relative to nearest positioned ancestor
- fixed = glued to viewport (ignores scroll)
- sticky = normal until scroll threshold, then fixed-like
- z-index only works on non-static positioned elements
- Always create stacking context with position + z-index or transform, filter, opacity < 1, etc.
How does it feel when you play with the demo? Any element behaving unexpectedly? Want to see a modal or tooltip example next?
Next possible lessons (just tell me):
- “real-world positioning: modal, tooltip, badge, dropdown”
- “z-index stacking context deep dive”
- “sticky sidebar vs fixed header”
- “common positioning bugs & fixes”
- “Flexbox + positioning combination patterns”
You’ve just unlocked one of the most powerful tools in CSS. Chai khatam? Fresh cup le aao — let’s keep going! 🚀 😄
