๐๏ธ HTML Document Structure
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>
<h1>Main Heading</h1>
<p>Some content here...</p>
</body>
</html><!-- HTML Document Structure โ Nested Build Infographic -->
<div class="infographic-htmlbuild">
<style>
.infographic-htmlbuild {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #faf8f4;
--paper-2: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--accent-deep:#1e40af;
--line: #ddd6c9;
--display-font: 'Fraunces', Georgia, serif;
--body-font: 'Inter', system-ui, sans-serif;
color: var(--ink);
font-family: var(--body-font);
max-width: 880px;
margin: 0 auto;
box-sizing: border-box;
line-height: 1.5;
background: transparent;
}
.infographic-htmlbuild[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-2: #1a1a22;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--accent-deep:#8fb3ff;
--line: #2c313d;
}
.infographic-htmlbuild * { box-sizing: border-box; }
.hb-header { margin-bottom: 1.5rem; text-align: center; }
.hb-header h1 {
font-family: var(--display-font);
font-size: clamp(1.6rem, 4vw, 2.3rem);
margin: 0 0 0.4rem 0;
font-weight: 600;
color: var(--ink);
}
.hb-header p { color: var(--ink-soft); margin: 0; font-size: 0.95rem; }
.hb-stage {
display: grid;
grid-template-columns: 1fr 280px;
gap: 2rem;
align-items: center;
min-height: 360px;
}
@media (max-width: 680px) {
.hb-stage { grid-template-columns: 1fr; min-height: auto; }
}
/* The visual block being built */
.hb-canvas {
position: relative;
display: flex;
align-items: center;
justify-content: center;
min-height: 340px;
}
.hb-box {
position: relative;
border: 2px solid var(--line);
border-radius: 8px;
background: var(--paper);
transition: all 0.45s cubic-bezier(.4,0,.2,1);
display: flex;
flex-direction: column;
}
/* nested boxes step up to --paper-2 so each level of nesting is
visibly distinct from its parent, in both light and dark mode */
.hb-box .hb-box { background: var(--paper-2); }
.hb-box .hb-tag-label {
position: absolute;
top: -0.75rem;
left: 1rem;
background: var(--paper);
padding: 0 0.5rem;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.78rem;
font-weight: 600;
color: var(--ink-soft);
opacity: 0;
transition: opacity 0.3s, color 0.3s, background 0.3s;
}
.hb-box .hb-box > .hb-tag-label { background: var(--paper-2); }
/* size states keyed by current step */
.hb-box.hb-html { width: 320px; height: 280px; padding: 1.6rem 1.2rem 1.2rem; }
.hb-box.hb-head { width: 260px; height: 70px; margin-bottom: 0.9rem; padding: 1.1rem 1rem 0.8rem; }
.hb-box.hb-title { width: 170px; height: 30px; padding: 0.5rem 0.7rem; font-size: 0.72rem; }
.hb-box.hb-body { width: 260px; flex: 1; padding: 1.1rem 1rem 0.8rem; }
.hb-box.hb-content{ width: 100%; height: 28px; font-size: 0.72rem; }
.hb-box.is-current { border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-soft); }
.hb-box.is-current .hb-tag-label { opacity: 1; color: var(--accent-deep); }
.hb-box.is-built .hb-tag-label { opacity: 1; }
.hb-box.is-dim { opacity: 0.3; }
.hb-inner-fill {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.75rem;
color: var(--ink-soft);
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
/* Controls */
.hb-controls { display: flex; flex-direction: column; gap: 0.6rem; }
.hb-step-card {
background: var(--paper-alt);
border: 1px solid var(--line);
border-left: 3px solid transparent;
border-radius: 4px;
padding: 0.75rem 0.9rem;
cursor: pointer;
transition: background 0.15s, border-color 0.15s, opacity 0.15s;
}
.hb-step-card.is-current { background: var(--accent-soft); border-left-color: var(--accent); }
.hb-step-card.is-locked { opacity: 0.45; cursor: not-allowed; }
.hb-step-card .step-tag {
font-family: 'IBM Plex Mono', monospace;
font-weight: 600;
font-size: 0.85rem;
color: var(--accent-deep);
}
.hb-step-card .step-desc {
margin-top: 0.3rem;
font-size: 0.82rem;
color: var(--ink-soft);
}
.hb-nav { display: flex; gap: 0.6rem; margin-top: 0.4rem; }
.hb-nav button {
font: inherit;
font-size: 0.82rem;
font-weight: 500;
background: var(--accent);
color: white;
border: none;
border-radius: 4px;
padding: 0.5rem 0.9rem;
cursor: pointer;
flex: 1;
transition: background 0.15s;
}
.hb-nav button:disabled { background: var(--line); cursor: not-allowed; color: var(--ink-soft); }
.hb-nav button.hb-prev { background: var(--paper); color: var(--ink); border: 1px solid var(--line); }
.hb-progress { display: flex; gap: 0.35rem; margin-top: 0.2rem; justify-content: center; }
.hb-progress .dot {
width: 8px; height: 8px; border-radius: 50%;
background: var(--line); transition: background 0.2s;
}
.hb-progress .dot.is-done { background: var(--accent); }
@media (prefers-reduced-motion: reduce) {
.infographic-htmlbuild *, .infographic-htmlbuild *::before, .infographic-htmlbuild *::after {
transition-duration: 0.001ms !important;
}
}
</style>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Fraunces:wght@500;600&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<div class="hb-header">
<p>Click a step or hit Next โ watch the document assemble itself.</p>
</div>
<div class="hb-stage">
<div class="hb-canvas">
<div class="hb-box hb-html" data-step="1">
<span class="hb-tag-label"><html></span>
<div class="hb-box hb-head" data-step="2">
<span class="hb-tag-label"><head></span>
<div class="hb-box hb-title" data-step="3">
<span class="hb-tag-label"><title></span>
</div>
</div>
<div class="hb-box hb-body" data-step="4">
<span class="hb-tag-label"><body></span>
<div class="hb-box hb-content" data-step="5">
<span class="hb-tag-label"><h1></span>
</div>
</div>
</div>
</div>
<div class="hb-controls">
<button type="button" class="hb-step-card" data-step="0">
<div class="step-tag"><!DOCTYPE html></div>
<div class="step-desc">Declares this as HTML5 โ the very first line, outside the box.</div>
</button>
<button type="button" class="hb-step-card" data-step="1">
<div class="step-tag"><html></div>
<div class="step-desc">The root. Every other element lives inside this one box.</div>
</button>
<button type="button" class="hb-step-card" data-step="2">
<div class="step-tag"><head></div>
<div class="step-desc">Metadata about the page โ nothing in here is visible.</div>
</button>
<button type="button" class="hb-step-card" data-step="3">
<div class="step-tag"><title></div>
<div class="step-desc">Sets the text on the browser tab.</div>
</button>
<button type="button" class="hb-step-card" data-step="4">
<div class="step-tag"><body></div>
<div class="step-desc">Where all visible content goes.</div>
</button>
<button type="button" class="hb-step-card" data-step="5">
<div class="step-tag"><h1>Hello!</h1></div>
<div class="step-desc">Actual visible content, nested inside body.</div>
</button>
<div class="hb-nav">
<button type="button" class="hb-prev">โ Back</button>
<button type="button" class="hb-next">Next โ</button>
</div>
<div class="hb-progress"></div>
</div>
</div>
<script>
(function () {
var wrapper = document.currentScript.closest('.infographic-htmlbuild')
|| document.querySelector('.infographic-htmlbuild');
var bodyAttr = document.body.getAttribute('data-darkmode');
var dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
var match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
(function() {
const root = document.currentScript.closest('.infographic-htmlbuild');
const TOTAL_STEPS = 5; // 0 (doctype) through 5
let current = 0;
const boxes = root.querySelectorAll('.hb-box');
const cards = root.querySelectorAll('.hb-step-card');
const progress = root.querySelector('.hb-progress');
const prevBtn = root.querySelector('.hb-prev');
const nextBtn = root.querySelector('.hb-next');
// build progress dots
for (let i = 0; i <= TOTAL_STEPS; i++) {
const d = document.createElement('span');
d.className = 'dot';
progress.appendChild(d);
}
const dots = progress.querySelectorAll('.dot');
function render() {
boxes.forEach(box => {
const step = Number(box.dataset.step);
box.classList.remove('is-current', 'is-built', 'is-dim');
if (step > current) {
box.classList.add('is-dim');
} else if (step === current) {
box.classList.add('is-current');
} else {
box.classList.add('is-built');
}
});
cards.forEach(card => {
const step = Number(card.dataset.step);
card.classList.toggle('is-current', step === current);
card.classList.toggle('is-locked', step > current + 1);
});
dots.forEach((dot, i) => dot.classList.toggle('is-done', i <= current));
prevBtn.disabled = current === 0;
nextBtn.disabled = current === TOTAL_STEPS;
nextBtn.textContent = current === TOTAL_STEPS ? 'Done โ' : 'Next โ';
}
function goTo(step) {
current = Math.max(0, Math.min(TOTAL_STEPS, step));
render();
}
cards.forEach(card => {
card.addEventListener('click', () => {
const step = Number(card.dataset.step);
if (step <= current + 1) goTo(step);
});
});
prevBtn.addEventListener('click', () => goTo(current - 1));
nextBtn.addEventListener('click', () => goTo(current + 1));
render();
})();
</script>
</div>.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!');
});