⚡️ Understanding Z Index
The z-index property controls the vertical stacking order of elements on the page (like layers).
🧠 Basic Rules
Higher z-index = closer to the viewer (on top).
Works only on elements with a position value other than static (relative, absolute, fixed, or sticky).
Elements with equal z-index stack in the order they appear in the DOM (later ones on top).
🔢 Syntax Example
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Z-Index Anatomy</title>
<style>
.infographic-zindex {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
box-sizing: border-box;
margin: 0 auto;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: var(--paper);
color: var(--ink);
border-radius: 14px;
overflow: hidden;
}
.infographic-zindex[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
}
.ha-inner { padding: 0.5rem; }
.ha-layout { display: flex; flex-direction: column; gap: 1.2rem; }
.ha-code-wrap {
background: #0b0b0b;
border: 1px solid #2a2a2a;
border-radius: 10px;
overflow: hidden;
}
.ha-filetabs {
display: flex;
background: #161616;
border-bottom: 1px solid #2a2a2a;
}
.ha-filetab {
border: none;
background: transparent;
color: #8a8a8a;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
font-size: 0.78rem;
font-weight: 700;
padding: 0.6rem 1rem;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.ha-filetab:hover { color: #e5e5e5; }
.ha-filetab.ha-tab-on {
color: #ffffff;
border-bottom-color: #5b8def;
background: #0b0b0b;
}
.ha-code {
padding: 1rem 1.8rem 1rem 0.9rem;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
font-size: 0.78rem;
line-height: 1.85;
overflow-x: auto;
}
.ha-codepane { display: none; }
.ha-codepane.ha-pane-on { display: block; }
.ha-line { display: block; white-space: pre; color: #8a8a8a; padding: 0.05rem 0.4rem; }
.ha-tag { color: #60a5fa; font-weight: 700; }
.ha-attr { color: #f2b96b; }
.ha-str { color: #9ece6a; }
.ha-sel { color: #f472b6; font-weight: 700; }
.ha-prop { color: #c586c0; }
.ha-val { color: #ce9178; }
.ha-z1 { color: #ff8a8a; font-weight: 800; }
.ha-z2 { color: #8ab4ff; font-weight: 800; }
.ha-stage {
background: var(--paper-alt);
border: 1px solid var(--line);
border-radius: 10px;
padding: 1.4rem;
position: relative;
height: 200px;
}
#box1, #box2 {
position: absolute;
width: 130px;
height: 130px;
color: white;
font-size: 0.85rem;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
box-sizing: border-box;
border-radius: 4px;
}
#box1 { background-color: #e03131; top: 20px; left: 20px; }
#box2 { background-color: #1c5ee0; top: 55px; left: 70px; color: #fff; }
.ha-controls { display: flex; flex-direction: column; gap: 0.55rem; }
.ha-slider-row {
display: grid;
grid-template-columns: 130px 1fr 30px;
align-items: center;
gap: 0.6rem;
}
.ha-slider-row label {
font-family: "SFMono-Regular", Consolas, monospace;
font-size: 0.75rem;
font-weight: 700;
}
.ha-slider-row.ha-row1 label { color: #e03131; }
.ha-slider-row.ha-row2 label { color: #1c5ee0; }
.ha-slider-row input[type="range"] { width: 100%; accent-color: var(--accent); }
.ha-slider-row .ha-val {
font-family: "SFMono-Regular", Consolas, monospace;
font-size: 0.75rem;
color: var(--ink-soft);
text-align: right;
}
.ha-note {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 0.78rem;
color: var(--ink-soft);
}
</style>
</head>
<body>
<div class="infographic-zindex" id="ha-root">
<div class="ha-inner">
<div class="ha-layout">
<div class="ha-code-wrap">
<div class="ha-filetabs">
<button class="ha-filetab ha-tab-on" id="tab-html">index.html</button>
<button class="ha-filetab" id="tab-css">style.css</button>
</div>
<div class="ha-code">
<div class="ha-codepane ha-pane-on" id="pane-html">
<span class="ha-line"><<span class="ha-tag">body</span>></span>
<span class="ha-line"> <<span class="ha-tag">div</span> <span class="ha-attr">class</span>=<span class="ha-str">"box1"</span>>Box 1 (Red)</<span class="ha-tag">div</span>></span>
<span class="ha-line"> <<span class="ha-tag">div</span> <span class="ha-attr">class</span>=<span class="ha-str">"box2"</span>>Box 2 (Blue)</<span class="ha-tag">div</span>></span>
<span class="ha-line"></<span class="ha-tag">body</span>></span>
</div>
<div class="ha-codepane" id="pane-css">
<span class="ha-line"><span class="ha-sel">.box1</span> {</span>
<span class="ha-line"> <span class="ha-prop">position</span>: <span class="ha-val">absolute</span>;</span>
<span class="ha-line"> <span class="ha-prop">z-index</span>: <span class="ha-z1" id="code-z1">1</span>;</span>
<span class="ha-line"> <span class="ha-prop">background-color</span>: <span class="ha-val">red</span>;</span>
<span class="ha-line">}</span>
<span class="ha-line"><span class="ha-sel">.box2</span> {</span>
<span class="ha-line"> <span class="ha-prop">position</span>: <span class="ha-val">absolute</span>;</span>
<span class="ha-line"> <span class="ha-prop">z-index</span>: <span class="ha-z2" id="code-z2">2</span>;</span>
<span class="ha-line"> <span class="ha-prop">background-color</span>: <span class="ha-val">blue</span>;</span>
<span class="ha-line">}</span>
</div>
</div>
</div>
<div class="ha-stage">
<div id="box1">Box 1 (Red)</div>
<div id="box2">Box 2 (Blue)</div>
</div>
<div class="ha-controls">
<div class="ha-slider-row ha-row1">
<label>.box1 z-index</label>
<input type="range" id="s-z1" min="0" max="5" value="1">
<span class="ha-val" id="v-z1">1</span>
</div>
<div class="ha-slider-row ha-row2">
<label>.box2 z-index</label>
<input type="range" id="s-z2" min="0" max="5" value="2">
<span class="ha-val" id="v-z2">2</span>
</div>
</div>
<div class="ha-note" id="note-out">Blue is on top because its z-index (2) is higher than red's (1) — even though red comes first in the HTML.</div>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.getElementById('ha-root');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
(function () {
const tabHtml = document.getElementById('tab-html');
const tabCss = document.getElementById('tab-css');
const paneHtml = document.getElementById('pane-html');
const paneCss = document.getElementById('pane-css');
tabHtml.addEventListener('click', () => {
tabHtml.classList.add('ha-tab-on');
tabCss.classList.remove('ha-tab-on');
paneHtml.classList.add('ha-pane-on');
paneCss.classList.remove('ha-pane-on');
});
tabCss.addEventListener('click', () => {
tabCss.classList.add('ha-tab-on');
tabHtml.classList.remove('ha-tab-on');
paneCss.classList.add('ha-pane-on');
paneHtml.classList.remove('ha-pane-on');
});
const sZ1 = document.getElementById('s-z1');
const sZ2 = document.getElementById('s-z2');
const vZ1 = document.getElementById('v-z1');
const vZ2 = document.getElementById('v-z2');
const codeZ1 = document.getElementById('code-z1');
const codeZ2 = document.getElementById('code-z2');
const box1 = document.getElementById('box1');
const box2 = document.getElementById('box2');
const note = document.getElementById('note-out');
function render() {
const z1 = +sZ1.value, z2 = +sZ2.value;
vZ1.textContent = z1;
vZ2.textContent = z2;
codeZ1.textContent = z1;
codeZ2.textContent = z2;
box1.style.zIndex = z1;
box2.style.zIndex = z2;
if (z1 === z2) {
note.textContent = 'Equal z-index values: the tiebreaker is source order, so Box 2 (later in the HTML) wins and sits on top.';
} else if (z1 > z2) {
note.textContent = 'Red is now on top because its z-index (' + z1 + ') is higher than blue\'s (' + z2 + '), overriding the HTML order.';
} else {
note.textContent = 'Blue is on top because its z-index (' + z2 + ') is higher than red\'s (' + z1 + ') — even though red comes first in the HTML.';
}
}
[sZ1, sZ2].forEach(el => el.addEventListener('input', render));
render();
})();
</script>
</body>
</html>.demo {
font-family: system-ui, sans-serif;
padding: 16px;
}
.demo h1 {
color: #2563eb;
margin: 0 0 8px;
}
.demo p {
color: #4b5563;
margin: 0;
}document.querySelector('.demo h1')?.addEventListener('click', () => {
alert('Hello from live preview!');
});📚 How Stacking Context Works
A stacking context is like a mini-universe of layers. When an element creates a new stacking context, all its children are stacked inside it.
Stacking context is triggered by:
positioned elements with z-index (not auto)
Elements with opacity < 1, transform, filter, will-change, etc.
👇 Example with Explanation
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Z-Index Nesting Anatomy</title>
<style>
.infographic-zindexnest {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
box-sizing: border-box;
margin: 0 auto;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: var(--paper);
color: var(--ink);
border-radius: 14px;
overflow: hidden;
}
.infographic-zindexnest[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
}
.ha-inner { padding: 0.5rem; }
.ha-layout { display: flex; flex-direction: column; gap: 1.2rem; }
.ha-code-wrap {
background: #0b0b0b;
border: 1px solid #2a2a2a;
border-radius: 10px;
overflow: hidden;
}
.ha-filetabs {
display: flex;
background: #161616;
border-bottom: 1px solid #2a2a2a;
}
.ha-filetab {
border: none;
background: transparent;
color: #8a8a8a;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
font-size: 0.78rem;
font-weight: 700;
padding: 0.6rem 1rem;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.ha-filetab:hover { color: #e5e5e5; }
.ha-filetab.ha-tab-on {
color: #ffffff;
border-bottom-color: #5b8def;
background: #0b0b0b;
}
.ha-code {
padding: 1rem 1.8rem 1rem 0.9rem;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
font-size: 0.76rem;
line-height: 1.8;
overflow-x: auto;
}
.ha-codepane { display: none; }
.ha-codepane.ha-pane-on { display: block; }
.ha-line { display: block; white-space: pre; color: #8a8a8a; padding: 0.05rem 0.4rem; }
.ha-tag { color: #60a5fa; font-weight: 700; }
.ha-attr { color: #f2b96b; }
.ha-str { color: #9ece6a; }
.ha-sel { color: #f472b6; font-weight: 700; }
.ha-prop { color: #c586c0; }
.ha-val { color: #ce9178; }
.ha-zp { color: #a3e635; font-weight: 800; }
.ha-z1 { color: #ff8a8a; font-weight: 800; }
.ha-z2 { color: #8ab4ff; font-weight: 800; }
.ha-stage {
background: var(--paper-alt);
border: 1px solid var(--line);
border-radius: 10px;
padding: 1.4rem;
display: flex;
justify-content: center;
}
#parent {
position: relative;
width: 220px;
height: 220px;
background-color: lightgray;
border: 2px solid black;
}
#child1, #child2 {
position: absolute;
width: 120px;
height: 120px;
color: white;
font-size: 0.8rem;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
box-sizing: border-box;
}
#child1 { background-color: red; top: 35px; left: 30px; }
#child2 { background-color: blue; top: 65px; left: 65px; }
.ha-controls { display: flex; flex-direction: column; gap: 0.55rem; }
.ha-slider-row {
display: grid;
grid-template-columns: 130px 1fr 30px;
align-items: center;
gap: 0.6rem;
}
.ha-slider-row label {
font-family: "SFMono-Regular", Consolas, monospace;
font-size: 0.75rem;
font-weight: 700;
}
.ha-slider-row.ha-row1 label { color: red; }
.ha-slider-row.ha-row2 label { color: blue; }
.ha-slider-row input[type="range"] { width: 100%; accent-color: var(--accent); }
.ha-slider-row .ha-val {
font-family: "SFMono-Regular", Consolas, monospace;
font-size: 0.75rem;
color: var(--ink-soft);
text-align: right;
}
.ha-note {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 0.78rem;
color: var(--ink-soft);
}
</style>
</head>
<body>
<div class="infographic-zindexnest" id="ha-root">
<div class="ha-inner">
<div class="ha-layout">
<div class="ha-code-wrap">
<div class="ha-filetabs">
<button class="ha-filetab ha-tab-on" id="tab-html">index.html</button>
<button class="ha-filetab" id="tab-css">style.css</button>
</div>
<div class="ha-code">
<div class="ha-codepane ha-pane-on" id="pane-html">
<span class="ha-line"><<span class="ha-tag">body</span>></span>
<span class="ha-line"> <<span class="ha-tag">div</span> <span class="ha-attr">class</span>=<span class="ha-str">"parent"</span>></span>
<span class="ha-line"> <<span class="ha-tag">div</span> <span class="ha-attr">class</span>=<span class="ha-str">"child1"</span>>Child 1 (Red)</<span class="ha-tag">div</span>></span>
<span class="ha-line"> <<span class="ha-tag">div</span> <span class="ha-attr">class</span>=<span class="ha-str">"child2"</span>>Child 2 (Blue)</<span class="ha-tag">div</span>></span>
<span class="ha-line"> </<span class="ha-tag">div</span>></span>
<span class="ha-line"></<span class="ha-tag">body</span>></span>
</div>
<div class="ha-codepane" id="pane-css">
<span class="ha-line"><span class="ha-sel">.parent</span> {</span>
<span class="ha-line"> <span class="ha-prop">position</span>: <span class="ha-val">relative</span>;</span>
<span class="ha-line"> <span class="ha-prop">z-index</span>: <span class="ha-zp">1</span>;</span>
<span class="ha-line"> <span class="ha-prop">background-color</span>: <span class="ha-val">lightgray</span>;</span>
<span class="ha-line">}</span>
<span class="ha-line"><span class="ha-sel">.child1</span> {</span>
<span class="ha-line"> <span class="ha-prop">position</span>: <span class="ha-val">absolute</span>;</span>
<span class="ha-line"> <span class="ha-prop">z-index</span>: <span class="ha-z1" id="code-z1">10</span>;</span>
<span class="ha-line"> <span class="ha-prop">background-color</span>: <span class="ha-val">red</span>;</span>
<span class="ha-line">}</span>
<span class="ha-line"><span class="ha-sel">.child2</span> {</span>
<span class="ha-line"> <span class="ha-prop">position</span>: <span class="ha-val">absolute</span>;</span>
<span class="ha-line"> <span class="ha-prop">z-index</span>: <span class="ha-z2" id="code-z2">5</span>;</span>
<span class="ha-line"> <span class="ha-prop">background-color</span>: <span class="ha-val">blue</span>;</span>
<span class="ha-line">}</span>
</div>
</div>
</div>
<div class="ha-stage">
<div id="parent">
<div id="child1">Child 1 (Red)</div>
<div id="child2">Child 2 (Blue)</div>
</div>
</div>
<div class="ha-controls">
<div class="ha-slider-row ha-row1">
<label>.child1 z-index</label>
<input type="range" id="s-z1" min="0" max="15" value="10">
<span class="ha-val" id="v-z1">10</span>
</div>
<div class="ha-slider-row ha-row2">
<label>.child2 z-index</label>
<input type="range" id="s-z2" min="0" max="15" value="5">
<span class="ha-val" id="v-z2">5</span>
</div>
</div>
<div class="ha-note" id="note-out">Red is on top: 10 > 5, both compared directly since they share the same parent's stacking context. Their z-index values never need to be compared against anything outside <code>.parent</code>.</div>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.getElementById('ha-root');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
(function () {
const tabHtml = document.getElementById('tab-html');
const tabCss = document.getElementById('tab-css');
const paneHtml = document.getElementById('pane-html');
const paneCss = document.getElementById('pane-css');
tabHtml.addEventListener('click', () => {
tabHtml.classList.add('ha-tab-on');
tabCss.classList.remove('ha-tab-on');
paneHtml.classList.add('ha-pane-on');
paneCss.classList.remove('ha-pane-on');
});
tabCss.addEventListener('click', () => {
tabCss.classList.add('ha-tab-on');
tabHtml.classList.remove('ha-tab-on');
paneCss.classList.add('ha-pane-on');
paneHtml.classList.remove('ha-pane-on');
});
const sZ1 = document.getElementById('s-z1');
const sZ2 = document.getElementById('s-z2');
const vZ1 = document.getElementById('v-z1');
const vZ2 = document.getElementById('v-z2');
const codeZ1 = document.getElementById('code-z1');
const codeZ2 = document.getElementById('code-z2');
const child1 = document.getElementById('child1');
const child2 = document.getElementById('child2');
const note = document.getElementById('note-out');
function render() {
const z1 = +sZ1.value, z2 = +sZ2.value;
vZ1.textContent = z1;
vZ2.textContent = z2;
codeZ1.textContent = z1;
codeZ2.textContent = z2;
child1.style.zIndex = z1;
child2.style.zIndex = z2;
if (z1 === z2) {
note.innerHTML = 'Equal z-index: source order breaks the tie, so <b style="color:blue">Child 2</b> (later in the HTML) sits on top.';
} else if (z1 > z2) {
note.innerHTML = '<b style="color:red">Red</b> is on top: ' + z1 + ' > ' + z2 + ', both compared directly since they share the same parent\'s stacking context.';
} else {
note.innerHTML = '<b style="color:blue">Blue</b> is on top: ' + z2 + ' > ' + z1 + ', both compared directly since they share the same parent\'s stacking context.';
}
}
[sZ1, sZ2].forEach(el => el.addEventListener('input', render));
render();
})();
</script>
</body>
</html>.demo {
font-family: system-ui, sans-serif;
padding: 16px;
}
.demo h1 {
color: #2563eb;
margin: 0 0 8px;
}
.demo p {
color: #4b5563;
margin: 0;
}document.querySelector('.demo h1')?.addEventListener('click', () => {
alert('Hello from live preview!');
});Even though child1 has a higher z-index (10) than child2 (5), both are inside .parent, which has a stacking level of z-index: 1.
So, if there's another element outside .parent with z-index: 2, it will appear above the entire .parent, no matter how high the children’s z-indexes are inside.
For Example:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Stacking Context Anatomy</title>
<style>
.infographic-stackctx {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
box-sizing: border-box;
margin: 0 auto;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: var(--paper);
color: var(--ink);
border-radius: 14px;
overflow: hidden;
}
.infographic-stackctx[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
}
.ha-inner { padding: 0.5rem; }
.ha-layout { display: flex; flex-direction: column; gap: 1.2rem; }
.ha-code-wrap {
background: #0b0b0b;
border: 1px solid #2a2a2a;
border-radius: 10px;
overflow: hidden;
}
.ha-filetabs {
display: flex;
background: #161616;
border-bottom: 1px solid #2a2a2a;
}
.ha-filetab {
border: none;
background: transparent;
color: #8a8a8a;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
font-size: 0.78rem;
font-weight: 700;
padding: 0.6rem 1rem;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.ha-filetab:hover { color: #e5e5e5; }
.ha-filetab.ha-tab-on {
color: #ffffff;
border-bottom-color: #5b8def;
background: #0b0b0b;
}
.ha-code {
padding: 1rem 1.8rem 1rem 0.9rem;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
font-size: 0.74rem;
line-height: 1.75;
overflow-x: auto;
}
.ha-codepane { display: none; }
.ha-codepane.ha-pane-on { display: block; }
.ha-line { display: block; white-space: pre; color: #8a8a8a; padding: 0.05rem 0.4rem; }
.ha-tag { color: #60a5fa; font-weight: 700; }
.ha-attr { color: #f2b96b; }
.ha-str { color: #9ece6a; }
.ha-sel { color: #f472b6; font-weight: 700; }
.ha-prop { color: #c586c0; }
.ha-val { color: #ce9178; }
.ha-zp { color: #a3e635; font-weight: 800; }
.ha-z1 { color: #ff8a8a; font-weight: 800; }
.ha-z2 { color: #8ab4ff; font-weight: 800; }
.ha-zo { color: #7ee787; font-weight: 800; }
.ha-stage {
background: var(--paper-alt);
border: 1px solid var(--line);
border-radius: 10px;
padding: 1.4rem;
position: relative;
height: 240px;
}
#parent {
position: relative;
width: 220px;
height: 220px;
background-color: lightgray;
border: 2px solid black;
}
#child1, #child2 {
position: absolute;
width: 120px;
height: 120px;
color: white;
font-size: 0.78rem;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
padding: 0.3rem;
box-sizing: border-box;
}
#child1 { background-color: red; top: 30px; left: 25px; }
#child2 { background-color: blue; top: 60px; left: 60px; }
#outside {
position: absolute;
width: 100px;
height: 100px;
background-color: green;
color: white;
font-size: 0.78rem;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
padding: 0.3rem;
box-sizing: border-box;
top: 60px;
left: 55px;
}
.ha-controls { display: flex; flex-direction: column; gap: 0.5rem; }
.ha-slider-row {
display: grid;
grid-template-columns: 135px 1fr 30px;
align-items: center;
gap: 0.6rem;
}
.ha-slider-row label {
font-family: "SFMono-Regular", Consolas, monospace;
font-size: 0.73rem;
font-weight: 700;
}
.ha-row-parent label { color: #6b7280; }
.ha-row-child1 label { color: red; }
.ha-row-outside label { color: green; }
.ha-slider-row input[type="range"] { width: 100%; accent-color: var(--accent); }
.ha-slider-row .ha-val {
font-family: "SFMono-Regular", Consolas, monospace;
font-size: 0.73rem;
color: var(--ink-soft);
text-align: right;
}
.ha-note {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 0.78rem;
color: var(--ink-soft);
}
</style>
</head>
<body>
<div class="infographic-stackctx" id="ha-root">
<div class="ha-inner">
<div class="ha-layout">
<div class="ha-code-wrap">
<div class="ha-filetabs">
<button class="ha-filetab ha-tab-on" id="tab-html">index.html</button>
<button class="ha-filetab" id="tab-css">style.css</button>
</div>
<div class="ha-code">
<div class="ha-codepane ha-pane-on" id="pane-html">
<span class="ha-line"><<span class="ha-tag">body</span>></span>
<span class="ha-line"> <<span class="ha-tag">div</span> <span class="ha-attr">class</span>=<span class="ha-str">"parent"</span>></span>
<span class="ha-line"> <<span class="ha-tag">div</span> <span class="ha-attr">class</span>=<span class="ha-str">"child1"</span>>Child 1 (Red)</<span class="ha-tag">div</span>></span>
<span class="ha-line"> <<span class="ha-tag">div</span> <span class="ha-attr">class</span>=<span class="ha-str">"child2"</span>>Child 2 (Blue)</<span class="ha-tag">div</span>></span>
<span class="ha-line"> </<span class="ha-tag">div</span>></span>
<span class="ha-line"> <<span class="ha-tag">div</span> <span class="ha-attr">class</span>=<span class="ha-str">"outside"</span>>Outside</<span class="ha-tag">div</span>></span>
<span class="ha-line"></<span class="ha-tag">body</span>></span>
</div>
<div class="ha-codepane" id="pane-css">
<span class="ha-line"><span class="ha-sel">.parent</span> {</span>
<span class="ha-line"> <span class="ha-prop">position</span>: <span class="ha-val">relative</span>;</span>
<span class="ha-line"> <span class="ha-prop">z-index</span>: <span class="ha-zp" id="code-zp">1</span>;</span>
<span class="ha-line">}</span>
<span class="ha-line"><span class="ha-sel">.child1</span> {</span>
<span class="ha-line"> <span class="ha-prop">position</span>: <span class="ha-val">absolute</span>;</span>
<span class="ha-line"> <span class="ha-prop">z-index</span>: <span class="ha-z1" id="code-z1">10</span>;</span>
<span class="ha-line">}</span>
<span class="ha-line"><span class="ha-sel">.child2</span> {</span>
<span class="ha-line"> <span class="ha-prop">position</span>: <span class="ha-val">absolute</span>;</span>
<span class="ha-line"> <span class="ha-prop">z-index</span>: <span class="ha-z2">5</span>;</span>
<span class="ha-line">}</span>
<span class="ha-line"><span class="ha-sel">.outside</span> {</span>
<span class="ha-line"> <span class="ha-prop">position</span>: <span class="ha-val">absolute</span>;</span>
<span class="ha-line"> <span class="ha-prop">z-index</span>: <span class="ha-zo" id="code-zo">2</span>;</span>
<span class="ha-line"> <span class="ha-prop">background-color</span>: <span class="ha-val">green</span>;</span>
<span class="ha-line">}</span>
</div>
</div>
</div>
<div class="ha-stage">
<div id="parent">
<div id="child1">Child 1 (Red)</div>
<div id="child2">Child 2 (Blue)</div>
</div>
<div id="outside">Outside</div>
</div>
<div class="ha-controls">
<div class="ha-slider-row ha-row-parent">
<label>.parent z-index</label>
<input type="range" id="s-zp" min="0" max="10" value="1">
<span class="ha-val" id="v-zp">1</span>
</div>
<div class="ha-slider-row ha-row-child1">
<label>.child1 z-index</label>
<input type="range" id="s-z1" min="0" max="20" value="10">
<span class="ha-val" id="v-z1">10</span>
</div>
<div class="ha-slider-row ha-row-outside">
<label>.outside z-index</label>
<input type="range" id="s-zo" min="0" max="10" value="2">
<span class="ha-val" id="v-zo">2</span>
</div>
</div>
<div class="ha-note" id="note-out">Green sits above the entire gray box — including red's z-index of 10 — because <code>.parent</code>'s <code>position: relative; z-index: 1</code> makes it a stacking context of its own. Green only ever compares against parent's 1, never reaching in to compare with child1's 10 directly.</div>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.getElementById('ha-root');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
(function () {
const tabHtml = document.getElementById('tab-html');
const tabCss = document.getElementById('tab-css');
const paneHtml = document.getElementById('pane-html');
const paneCss = document.getElementById('pane-css');
tabHtml.addEventListener('click', () => {
tabHtml.classList.add('ha-tab-on');
tabCss.classList.remove('ha-tab-on');
paneHtml.classList.add('ha-pane-on');
paneCss.classList.remove('ha-pane-on');
});
tabCss.addEventListener('click', () => {
tabCss.classList.add('ha-tab-on');
tabHtml.classList.remove('ha-tab-on');
paneCss.classList.add('ha-pane-on');
paneHtml.classList.remove('ha-pane-on');
});
const sZp = document.getElementById('s-zp');
const sZ1 = document.getElementById('s-z1');
const sZo = document.getElementById('s-zo');
const vZp = document.getElementById('v-zp');
const vZ1 = document.getElementById('v-z1');
const vZo = document.getElementById('v-zo');
const codeZp = document.getElementById('code-zp');
const codeZ1 = document.getElementById('code-z1');
const codeZo = document.getElementById('code-zo');
const parent = document.getElementById('parent');
const child1 = document.getElementById('child1');
const outside = document.getElementById('outside');
const note = document.getElementById('note-out');
function render() {
const zp = +sZp.value, z1 = +sZ1.value, zo = +sZo.value;
vZp.textContent = zp; vZ1.textContent = z1; vZo.textContent = zo;
codeZp.textContent = zp; codeZ1.textContent = z1; codeZo.textContent = zo;
parent.style.zIndex = zp;
child1.style.zIndex = z1;
outside.style.zIndex = zo;
if (zo > zp) {
note.innerHTML = 'Green (' + zo + ') sits above the entire gray box (' + zp + ') — including red\'s z-index of ' + z1 + ' — because <code>.parent</code>\'s own z-index makes it a self-contained stacking context. Green never compares against child1\'s ' + z1 + ' directly, only against parent\'s ' + zp + '.';
} else if (zo === zp) {
note.innerHTML = 'Equal z-index (' + zo + ' = ' + zp + '): source order breaks the tie — <code>.outside</code> comes later in the HTML, so it still wins, regardless of what\'s happening inside <code>.parent</code>.';
} else {
note.innerHTML = 'Now green (' + zo + ') sits below the gray box (' + zp + ') entirely — it doesn\'t matter that child1\'s z-index (' + z1 + ') is much higher, because that comparison never happens; parent\'s own z-index is all that counts from the outside.';
}
}
[sZp, sZ1, sZo].forEach(el => el.addEventListener('input', render));
render();
})();
</script>
</body>
</html>.demo {
font-family: system-ui, sans-serif;
padding: 16px;
}
.demo h1 {
color: #2563eb;
margin: 0 0 8px;
}
.demo p {
color: #4b5563;
margin: 0;
}document.querySelector('.demo h1')?.addEventListener('click', () => {
alert('Hello from live preview!');
});🧩 Real-World Use Cases
Modal popups → z-index: 9999
Sticky navbars above content → z-index: 100
Tooltips and dropdowns → z-index: 500