feat(types): add treemap for part-of-whole by area (#87)

Add Treemap as the 28th visual type with light, dark, and editorial variants; area, label, and information-marker verification; synchronized documentation and manifests; and plugin version 2.5.0.
This commit is contained in:
0xDarkMatter
2026-08-18 00:50:19 -07:00
committed by GitHub
parent 72f17665cb
commit de75c0adb5
21 changed files with 1072 additions and 36 deletions
+6 -4
View File
@@ -1,6 +1,6 @@
---
name: diagram-design
description: Create branded architecture, IT current-state, flowchart, sequence, state machine, ER/data model, timeline, swimlane, quadrant, radar/spider, loop/flywheel, nested, tree, org chart, layer stack, Venn, pyramid/funnel, bar, line, Gantt and scatter charts, high-level, process, medallion, data flow, DP integration, or DP security matrix diagrams as standalone HTML/SVG/PNG. Redraw .drawio/.drawio.png/.drawio.svg or Mermaid .mmd sources at a chosen size/detail; onboard brand tokens from a website; add semantic patterns, callouts, accessible motion, or sketchy/hand-drawn styling.
description: Create branded architecture, IT current-state, flowchart, sequence, state machine, ER/data model, timeline, swimlane, quadrant, radar/spider, loop/flywheel, nested, tree, org chart, layer stack, Venn, pyramid/funnel, treemap, bar, line, Gantt and scatter charts, high-level, process, medallion, data flow, DP integration, or DP security matrix diagrams as standalone HTML/SVG/PNG. Redraw .drawio/.drawio.png/.drawio.svg or Mermaid .mmd sources at a chosen size/detail; onboard brand tokens from a website; add semantic patterns, callouts, accessible motion, or sketchy/hand-drawn styling.
license: MIT
metadata:
version: "2.4"
@@ -10,7 +10,7 @@ metadata:
Create visual diagrams as self-contained HTML files with inline SVG and CSS, following an opinionated editorial design system.
Twenty-seven visual types. Semantic patterns describe behavior independently; type references describe layout. Details load from `references/` only when selected.
Twenty-eight visual types. Semantic patterns describe behavior independently; type references describe layout. Details load from `references/` only when selected.
---
@@ -49,7 +49,7 @@ Applied to schematics:
## 2. When to Use
Use for any of the 27 visual types (§3) when a reader will learn more from a visual than from prose, a table, or a bulleted list.
Use for any of the 28 visual types (§3) when a reader will learn more from a visual than from prose, a table, or a bulleted list.
**Don't use for:**
@@ -78,7 +78,7 @@ When behavior, state, enforcement, or risk carries the meaning, first load [`ref
The pattern owns semantic primitives and its tighter budget; the type owns layout grammar. Use [`references/animation.md`](references/animation.md) only when motion is requested or materially clarifies ordered change; static remains the default.
### Visual-type guide (27)
### Visual-type guide (28)
| If you're showing… | Use | Reference |
|---|---|---|
@@ -100,6 +100,7 @@ The pattern owns semantic primitives and its tighter budget; the type owns layou
| Overlap between sets | **Venn** | [type-venn.md](references/type-venn.md) |
| Ranked hierarchy or conversion drop-off | **Pyramid / funnel** | [type-pyramid.md](references/type-pyramid.md) |
| Quantitative comparison across categories | **Bar chart** | [type-bar.md](references/type-bar.md) |
| Part-of-whole where the relative sizes are the story | **Treemap** | [type-treemap.md](references/type-treemap.md) |
| Continuous trends over time | **Line chart** | [type-line.md](references/type-line.md) |
| Tasks and phases on a timeline | **Gantt** | [type-gantt.md](references/type-gantt.md) |
| Distribution and correlation between two variables | **Scatter plot** | [type-scatter.md](references/type-scatter.md) |
@@ -378,6 +379,7 @@ Quick check: if a coordinate ends in 1, 2, 3, 5, 6, 7, 9 — fix it.
| Max radar series | 5 |
| Max focal radar series | 1 |
| Max bars (bar chart) | 8 |
| Max cells (treemap) | 8 |
| Max series (line chart) | 5 |
| Max tasks (Gantt) | 12 |
| Max points (scatter plot) | 30 |
@@ -0,0 +1,107 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>World population · Treemap · dark</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--color-paper: #2d3142;
--color-ink: #f5f5f5;
--color-muted: #bfc0c0;
--color-accent: #f08a59;
--font-sans: 'Geist', system-ui, sans-serif;
--font-serif: 'Instrument Serif', serif;
--font-mono: 'Geist Mono', ui-monospace, monospace;
}
body { font-family: var(--font-sans); background: var(--color-paper); color: var(--color-ink); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 3rem 2rem; }
.frame { max-width: 1200px; width: 100%; }
.eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.15; margin-bottom: 1.5rem; }
svg { width: 100%; min-width: 760px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Treemap · Diagram Design</p>
<h1>World population · six continents</h1>
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="treemap-dark-title treemap-dark-desc">
<title id="treemap-dark-title">World population · six continents</title>
<desc id="treemap-dark-desc">Treemap of world population by continent in mid-2023, where cell area is population; Asia holds about 59 percent of the world total and Oceania, at under one percent, is too small to label.</desc>
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(245,245,245,0.08)"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="#2d3142"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- ─── CELLS ─── Squarified layout on a 4px grid. Area is the only
encoding: every cell is within 2.7% of its true share, measured as
RELATIVE error, because a 0.1pp slip is nothing on Asia and a third
of Oceania. Verified by scripts/verify-treemap.py. The non-focal
cells run a rank-ordered ink ramp, so their order survives greyscale;
the focal cell is marked by its accent stroke, not by tone. ─── -->
<!-- Asia FOCAL: 532×380 -->
<rect x="40" y="40" width="532" height="380" rx="2" fill="#2d3142"/>
<rect x="40" y="40" width="532" height="380" rx="2" fill="rgba(240,138,89,0.16)" stroke="#f08a59" stroke-width="1.5"/>
<text x="56" y="68" fill="#f5f5f5" font-size="14" font-weight="600" font-family="'Geist', sans-serif">Asia</text>
<text x="56" y="88" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace">4.78B · 59% of world</text>
<!-- Africa: 252×252 -->
<rect x="576" y="40" width="252" height="252" rx="2" fill="#2d3142"/>
<rect x="576" y="40" width="252" height="252" rx="2" fill="rgba(245,245,245,0.14)" stroke="rgba(245,245,245,0.30)" stroke-width="1"/>
<text x="592" y="68" fill="#f5f5f5" font-size="13" font-weight="600" font-family="'Geist', sans-serif">Africa</text>
<text x="592" y="88" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace">1.48B · 18%</text>
<!-- Europe: 124×252 -->
<rect x="832" y="40" width="124" height="252" rx="2" fill="#2d3142"/>
<rect x="832" y="40" width="124" height="252" rx="2" fill="rgba(245,245,245,0.11)" stroke="rgba(245,245,245,0.30)" stroke-width="1"/>
<text x="848" y="68" fill="#f5f5f5" font-size="13" font-weight="600" font-family="'Geist', sans-serif">Europe</text>
<text x="848" y="88" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace">0.75B · 9%</text>
<!-- North America: 208×124 -->
<rect x="576" y="296" width="208" height="124" rx="2" fill="#2d3142"/>
<rect x="576" y="296" width="208" height="124" rx="2" fill="rgba(245,245,245,0.08)" stroke="rgba(245,245,245,0.30)" stroke-width="1"/>
<text x="592" y="324" fill="#f5f5f5" font-size="12" font-weight="600" font-family="'Geist', sans-serif">North America</text>
<text x="592" y="344" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace">0.61B · 8%</text>
<!-- South America: 148×124 -->
<rect x="788" y="296" width="148" height="124" rx="2" fill="#2d3142"/>
<rect x="788" y="296" width="148" height="124" rx="2" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.30)" stroke-width="1"/>
<text x="804" y="324" fill="#f5f5f5" font-size="12" font-weight="600" font-family="'Geist', sans-serif">South America</text>
<text x="804" y="344" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace">0.43B · 5%</text>
<!-- Oceania: 16×124. Too narrow for a label, so it carries the info mark
rather than sideways text — the only rotated glyph in the figure would
read as a mistake. The cell keeps its honest area and is named in the
legend: a treemap must not silently omit a part of the whole it claims
to show, but it need not label what it cannot label well. -->
<rect x="940" y="296" width="16" height="124" rx="2" fill="#2d3142"/>
<rect x="940" y="296" width="16" height="124" rx="2" fill="rgba(245,245,245,0.04)" stroke="rgba(245,245,245,0.30)" stroke-width="1"/>
<circle cx="948" cy="358" r="5" fill="#f5f5f5"/>
<text x="948" y="361" fill="#2d3142" font-size="7" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">i</text>
<!-- Legend -->
<line x1="40" y1="462" x2="960" y2="462" stroke="rgba(245,245,245,0.08)" stroke-width="0.8"/>
<text x="40" y="478" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<text x="960" y="478" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.06em" text-anchor="end">AREA = POPULATION · OUR WORLD IN DATA CONTINENTS (UN WPP) · MID-2023 · 8.09B TOTAL</text>
<rect x="40" y="488" width="16" height="10" rx="2" fill="rgba(240,138,89,0.16)" stroke="#f08a59" stroke-width="1"/>
<text x="64" y="497" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Asia · the focal share</text>
<rect x="240" y="488" width="16" height="10" rx="2" fill="rgba(245,245,245,0.14)" stroke="rgba(245,245,245,0.30)" stroke-width="1"/>
<text x="264" y="497" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Other continents · darker is larger</text>
<circle cx="484" cy="493" r="5" fill="#f5f5f5"/>
<text x="484" y="496" fill="#2d3142" font-size="7" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">i</text>
<text x="500" y="497" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Oceania · 0.05B, 0.6% — too small to label</text>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,134 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>World population · Treemap</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root { --color-paper:#f5f5f5; --color-paper-2:#ececec; --color-ink:#2d3142; --color-muted:#4f5d75; --color-soft:#7a8399; --color-rule:rgba(45,49,66,0.12); --color-accent:#eb6c36; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
.container { max-width: 1200px; margin: 0 auto; }
.header { margin-bottom: 2.5rem; }
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
svg { width: 100%; min-width: 760px; display: block; }
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
@media (max-width: 720px) { .cards { grid-template-columns: 1fr; } }
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(45,49,66,0.08); }
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
.card-dot.coral { background: var(--color-accent); } .card-dot.ink { background: var(--color-ink); } .card-dot.muted { background: var(--color-muted); }
.card h3 { font-size: 0.875rem; font-weight: 600; }
.card p { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; }
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(45,49,66,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<p class="header-eyebrow">Treemap · Diagram Design</p>
<h1>World population · six continents</h1>
<p class="subtitle">Population by continent, mid-2023. Asia holds 4.78 billion people — 59% of everyone alive, and more than the other five continents put together.</p>
</div>
<div class="diagram-container">
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="treemap-full-title treemap-full-desc">
<title id="treemap-full-title">World population · six continents</title>
<desc id="treemap-full-desc">Treemap of world population by continent in mid-2023, where cell area is population; Asia holds about 59 percent of the world total and Oceania, at under one percent, is too small to label.</desc>
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- ─── CELLS ─── Squarified layout on a 4px grid. Area is the only
encoding: every cell is within 2.7% of its true share, measured as
RELATIVE error, because a 0.1pp slip is nothing on Asia and a third
of Oceania. Verified by scripts/verify-treemap.py. The non-focal
cells run a rank-ordered ink ramp, so their order survives greyscale;
the focal cell is marked by its accent stroke, not by tone. ─── -->
<!-- Asia FOCAL: 532×380 -->
<rect x="40" y="40" width="532" height="380" rx="2" fill="#f5f5f5"/>
<rect x="40" y="40" width="532" height="380" rx="2" fill="rgba(235,108,54,0.16)" stroke="#eb6c36" stroke-width="1.5"/>
<text x="56" y="68" fill="#2d3142" font-size="14" font-weight="600" font-family="'Geist', sans-serif">Asia</text>
<text x="56" y="88" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">4.78B · 59% of world</text>
<!-- Africa: 252×252 -->
<rect x="576" y="40" width="252" height="252" rx="2" fill="#f5f5f5"/>
<rect x="576" y="40" width="252" height="252" rx="2" fill="rgba(45,49,66,0.16)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text x="592" y="68" fill="#2d3142" font-size="13" font-weight="600" font-family="'Geist', sans-serif">Africa</text>
<text x="592" y="88" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">1.48B · 18%</text>
<!-- Europe: 124×252 -->
<rect x="832" y="40" width="124" height="252" rx="2" fill="#f5f5f5"/>
<rect x="832" y="40" width="124" height="252" rx="2" fill="rgba(45,49,66,0.13)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text x="848" y="68" fill="#2d3142" font-size="13" font-weight="600" font-family="'Geist', sans-serif">Europe</text>
<text x="848" y="88" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">0.75B · 9%</text>
<!-- North America: 208×124 -->
<rect x="576" y="296" width="208" height="124" rx="2" fill="#f5f5f5"/>
<rect x="576" y="296" width="208" height="124" rx="2" fill="rgba(45,49,66,0.10)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text x="592" y="324" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist', sans-serif">North America</text>
<text x="592" y="344" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">0.61B · 8%</text>
<!-- South America: 148×124 -->
<rect x="788" y="296" width="148" height="124" rx="2" fill="#f5f5f5"/>
<rect x="788" y="296" width="148" height="124" rx="2" fill="rgba(45,49,66,0.07)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text x="804" y="324" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist', sans-serif">South America</text>
<text x="804" y="344" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">0.43B · 5%</text>
<!-- Oceania: 16×124. Too narrow for a label, so it carries the info mark
rather than sideways text — the only rotated glyph in the figure would
read as a mistake. The cell keeps its honest area and is named in the
legend: a treemap must not silently omit a part of the whole it claims
to show, but it need not label what it cannot label well. -->
<rect x="940" y="296" width="16" height="124" rx="2" fill="#f5f5f5"/>
<rect x="940" y="296" width="16" height="124" rx="2" fill="rgba(45,49,66,0.04)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<circle cx="948" cy="358" r="5" fill="#2d3142"/>
<text x="948" y="361" fill="#f5f5f5" font-size="7" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">i</text>
<!-- Legend -->
<line x1="40" y1="462" x2="960" y2="462" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="478" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<text x="960" y="478" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.06em" text-anchor="end">AREA = POPULATION · OUR WORLD IN DATA CONTINENTS (UN WPP) · MID-2023 · 8.09B TOTAL</text>
<rect x="40" y="488" width="16" height="10" rx="2" fill="rgba(235,108,54,0.16)" stroke="#eb6c36" stroke-width="1"/>
<text x="64" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Asia · the focal share</text>
<rect x="240" y="488" width="16" height="10" rx="2" fill="rgba(45,49,66,0.16)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text x="264" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Other continents · darker is larger</text>
<circle cx="484" cy="493" r="5" fill="#2d3142"/>
<text x="484" y="496" fill="#f5f5f5" font-size="7" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">i</text>
<text x="500" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Oceania · 0.05B, 0.6% — too small to label</text>
</svg>
</div>
<div class="cards">
<div class="card">
<div class="card-header"><span class="card-dot coral"></span><h3>One cell carries the story</h3></div>
<p>Asia is the focal cell because it is the finding, not because it sorts first. Nearly six of every ten people alive are inside that rectangle.</p>
</div>
<div class="card">
<div class="card-header"><span class="card-dot ink"></span><h3>How to read it</h3></div>
<p>Cells are laid out squarified, largest first, so aspect ratios stay near 1 and areas stay comparable by eye. Among the other five, darker means larger.</p>
</div>
<div class="card">
<div class="card-header"><span class="card-dot muted"></span><h3>What the chart doesn't show</h3></div>
<p>Population, not growth. Africa's cell is under a third of Asia's today, and it is the one adding people fastest — this is one moment, not a direction.</p>
</div>
</div>
<div class="footer">
<span>world population · six continents</span>
<span>example · diagram design</span>
</div>
</div>
</body>
</html>
@@ -0,0 +1,107 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>World population · Treemap</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--color-paper: #f5f5f5;
--color-ink: #2d3142;
--color-muted: #4f5d75;
--color-accent: #eb6c36;
--font-sans: 'Geist', system-ui, sans-serif;
--font-serif: 'Instrument Serif', serif;
--font-mono: 'Geist Mono', ui-monospace, monospace;
}
body { font-family: var(--font-sans); background: var(--color-paper); color: var(--color-ink); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 3rem 2rem; }
.frame { max-width: 1200px; width: 100%; }
.eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.15; margin-bottom: 1.5rem; }
svg { width: 100%; min-width: 760px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Treemap · Diagram Design</p>
<h1>World population · six continents</h1>
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="treemap-title treemap-desc">
<title id="treemap-title">World population · six continents</title>
<desc id="treemap-desc">Treemap of world population by continent in mid-2023, where cell area is population; Asia holds about 59 percent of the world total and Oceania, at under one percent, is too small to label.</desc>
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- ─── CELLS ─── Squarified layout on a 4px grid. Area is the only
encoding: every cell is within 2.7% of its true share, measured as
RELATIVE error, because a 0.1pp slip is nothing on Asia and a third
of Oceania. Verified by scripts/verify-treemap.py. The non-focal
cells run a rank-ordered ink ramp, so their order survives greyscale;
the focal cell is marked by its accent stroke, not by tone. ─── -->
<!-- Asia FOCAL: 532×380 -->
<rect x="40" y="40" width="532" height="380" rx="2" fill="#f5f5f5"/>
<rect x="40" y="40" width="532" height="380" rx="2" fill="rgba(235,108,54,0.16)" stroke="#eb6c36" stroke-width="1.5"/>
<text x="56" y="68" fill="#2d3142" font-size="14" font-weight="600" font-family="'Geist', sans-serif">Asia</text>
<text x="56" y="88" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">4.78B · 59% of world</text>
<!-- Africa: 252×252 -->
<rect x="576" y="40" width="252" height="252" rx="2" fill="#f5f5f5"/>
<rect x="576" y="40" width="252" height="252" rx="2" fill="rgba(45,49,66,0.16)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text x="592" y="68" fill="#2d3142" font-size="13" font-weight="600" font-family="'Geist', sans-serif">Africa</text>
<text x="592" y="88" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">1.48B · 18%</text>
<!-- Europe: 124×252 -->
<rect x="832" y="40" width="124" height="252" rx="2" fill="#f5f5f5"/>
<rect x="832" y="40" width="124" height="252" rx="2" fill="rgba(45,49,66,0.13)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text x="848" y="68" fill="#2d3142" font-size="13" font-weight="600" font-family="'Geist', sans-serif">Europe</text>
<text x="848" y="88" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">0.75B · 9%</text>
<!-- North America: 208×124 -->
<rect x="576" y="296" width="208" height="124" rx="2" fill="#f5f5f5"/>
<rect x="576" y="296" width="208" height="124" rx="2" fill="rgba(45,49,66,0.10)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text x="592" y="324" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist', sans-serif">North America</text>
<text x="592" y="344" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">0.61B · 8%</text>
<!-- South America: 148×124 -->
<rect x="788" y="296" width="148" height="124" rx="2" fill="#f5f5f5"/>
<rect x="788" y="296" width="148" height="124" rx="2" fill="rgba(45,49,66,0.07)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text x="804" y="324" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist', sans-serif">South America</text>
<text x="804" y="344" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">0.43B · 5%</text>
<!-- Oceania: 16×124. Too narrow for a label, so it carries the info mark
rather than sideways text — the only rotated glyph in the figure would
read as a mistake. The cell keeps its honest area and is named in the
legend: a treemap must not silently omit a part of the whole it claims
to show, but it need not label what it cannot label well. -->
<rect x="940" y="296" width="16" height="124" rx="2" fill="#f5f5f5"/>
<rect x="940" y="296" width="16" height="124" rx="2" fill="rgba(45,49,66,0.04)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<circle cx="948" cy="358" r="5" fill="#2d3142"/>
<text x="948" y="361" fill="#f5f5f5" font-size="7" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">i</text>
<!-- Legend -->
<line x1="40" y1="462" x2="960" y2="462" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="478" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<text x="960" y="478" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.06em" text-anchor="end">AREA = POPULATION · OUR WORLD IN DATA CONTINENTS (UN WPP) · MID-2023 · 8.09B TOTAL</text>
<rect x="40" y="488" width="16" height="10" rx="2" fill="rgba(235,108,54,0.16)" stroke="#eb6c36" stroke-width="1"/>
<text x="64" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Asia · the focal share</text>
<rect x="240" y="488" width="16" height="10" rx="2" fill="rgba(45,49,66,0.16)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text x="264" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Other continents · darker is larger</text>
<circle cx="484" cy="493" r="5" fill="#2d3142"/>
<text x="484" y="496" fill="#f5f5f5" font-size="7" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">i</text>
<text x="500" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Oceania · 0.05B, 0.6% — too small to label</text>
</svg>
</div>
</body>
</html>
+3
View File
@@ -276,6 +276,9 @@
<button class="tab new" data-type="high-level-vertical">
<span class="eyebrow">28</span>High-Level / Parametric
</button>
<button class="tab new" data-type="treemap">
<span class="eyebrow">29</span>Treemap
</button>
<button class="tab new" data-type="dp-security-matrix">
<span class="eyebrow">32</span>DP security matrix
</button>
@@ -159,7 +159,7 @@ Write the new tokens to `style-guide.md`. Suggest running the `/regenerate-examp
After onboarding, the user should:
1. Open `assets/index.html` (gallery) and confirm the new palette feels coherent across all 27 types.
1. Open `assets/index.html` (gallery) and confirm the new palette feels coherent across all 28 types.
2. If any type looks off, they usually need to tune `muted` (often too dark or too light against the new `paper`).
---
@@ -1,6 +1,6 @@
# Semantic patterns
Semantic patterns describe **what a system does**; the 27 visual types describe **how information is arranged**. Choose a pattern first when behavior, state, enforcement, or risk is load-bearing, then use its nearest visual type as the layout grammar. If no pattern matches, choose a visual type directly.
Semantic patterns describe **what a system does**; the 28 visual types describe **how information is arranged**. Choose a pattern first when behavior, state, enforcement, or risk is load-bearing, then use its nearest visual type as the layout grammar. If no pattern matches, choose a visual type directly.
Use one primary pattern per figure. A second pattern may supply at most one supporting primitive; if both need full treatment, split overview and detail. Labels and outcomes must remain complete in a static frame.
@@ -0,0 +1,56 @@
# Treemap
**Best for:** part-of-whole where the *relative sizes are the story* — disk and bundle usage, budget or spend breakdowns, market share, population, time allocation. Use when a single total decomposes into parts and the reader's question is "what dominates, and by how much?"
Not for: ranked lists where exact values matter more than proportion (use a **bar chart**), containment or scope relationships with no quantity (use **nested**), or a hierarchy you need to trace parent-to-child (use a **tree**).
## Layout conventions
- **Plot area:** `x` 40 → 956, `y` 40 → 420 inside a `0 0 1000 500` viewBox — the same vertical rhythm as bar, line and scatter, so the legend block sits where a reader of those types already expects it (rule at `y=462`, `LEGEND` at `478`, keys at `488`).
- **Squarified layout** (Bruls et al., 2000): sort descending, lay each row against the *shorter* side of the remaining rectangle. Aspect ratios stay near 1, which is what makes two areas comparable by eye. Never lay cells out in simple stripes — long thin cells cannot be compared.
- **Cell count:** 4–8. Past 8, the tail becomes unlabelable slivers — group the tail into a single explicit "Other" cell and name what it contains in the source line.
- **4px grid:** cell edges snap to the grid like everything else, with a 4px gutter between cells. Snapping and gutters both move area, so check the result — and check it as **relative** error (`(drawn − true) ÷ true`), not percentage points. A 0.1pp slip is nothing on a 59% cell and a quarter of a 0.5% cell; absolute error hides exactly the mistake you need to catch. Keep every cell within a few percent of its true share — the shipped example is within 2.7%, its worst case being the sliver, where the grid cannot do better — and state the encoding in the source line (`AREA = POPULATION`). `scripts/verify-treemap.py` gates this.
- **Fill:** a rank-ordered `ink` opacity ramp (e.g. `0.16 → 0.04`), so the non-focal order survives greyscale printing and colour-blind readers. One accent cell only — the editorially focal one, not automatically the largest. Note what this does *not* buy you: the focal cell is painted off-ramp, so in greyscale it lands wherever its tint happens to fall, not at its rank. Identify it by the accent stroke, and never let tone carry meaning the area doesn't already carry.
- **Stroke:** 1px `ink @ 0.30` hairline on every cell; the focal cell takes a 1.5px `accent` stroke.
- **Labels sit inside the cell**, top-left, 16px in from the edge: name in Geist 12–14px 600, value in Geist Mono 9px on the next line. Three tiers by cell size:
- large — name + value + share (`4.78B · 59% of world`)
- medium — name + value
- small — a 3-letter mono abbreviation, if one reads honestly
- sliver — **no text.** When the cell is at least 12×12px, use a filled `ink` disc, `r=5`, centred in the cell, carrying a paper-coloured `i`, with the cell's name and share spelled out in the legend. Below 12px on either axis, omit the in-cell mark and identify the sliver by position in the legend; a fixed-size disc must never cross the cell boundary. `scripts/verify-treemap.py` checks marker containment. Resist rotating a label to make it fit: a single sideways word among five upright ones reads as a mistake before it reads as data, and its centring is a trap — `text-anchor="middle"` centres along the *baseline*, which a quarter-turn maps to the cell's long axis, leaving nothing centring the cap-height band across the narrow one.
- Never shrink a cell to fit its label. The cell size is the data; the label is commentary.
- **Contrast:** compute the ceiling against the token you actually ship. The 9px value line is `muted`, not `ink`, and `muted` needs a lighter cell than `ink` does: measured against the composited fill, the top of the ramp can go to **0.16 on light paper and 0.14 on dark** before it drops under 4.5:1. (`ink` would tolerate 0.20 — which is exactly the number you will write down if you check the wrong token.) A mid-tone fill — solid accent, or 50% ink — fails against both light and dark text; use the tint-plus-stroke pattern instead. The source line takes `muted` too: `soft` measures 3.48:1 on paper and never reaches AA anywhere on this ramp.
- **Legend:** the house block (rule, `LEGEND`, keys), naming the focal cell, the direction of the ink ramp, and any cell carrying an info mark. The source line rides the same row as `LEGEND`, right-aligned in mono 8px, stating what area encodes plus the dataset and its date.
### Cell element pattern
```svg
<!-- Opaque paper mask prevents the dot pattern showing through the tint -->
<rect x="X" y="Y" width="W" height="H" rx="2" fill="#f5f5f5"/>
<!-- Cell body -->
<rect x="X" y="Y" width="W" height="H" rx="2" fill="rgba(45,49,66,0.18)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text x="X+16" y="Y+28" fill="#2d3142" font-size="13" font-weight="600" font-family="'Geist', sans-serif">NAME</text>
<text x="X+16" y="Y+46" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">VALUE · SHARE</text>
```
Focal cell: replace the fill with `rgba(235,108,54,0.16)` and the stroke with `#eb6c36` at 1.5px.
## Honest-data rule
**Area is the only encoding.** Never clip, floor, or log-scale a cell to make it visible, and never drop a cell because it is small — a treemap claims to show a whole, so an omitted part makes the picture a lie. A part too small to label gets a legend entry and, only when the cell can contain it, an info mark; parts too small to draw get merged into one honest, named "Other". If several cells are invisible at the target size, the data wants a bar chart.
Watch the smallest cell hardest: it is the one that grid snapping and gutters distort most, and the one nobody checks. Beware, too, the rounding you *display*: six values each rounded up can sum past the total you printed underneath them. Either carry enough precision that the parts reconcile, or say plainly in the source line that they don't (`PARTS ROUNDED, MAY NOT SUM`).
## Anti-patterns
- More than 8 cells without an "Other" bucket (unlabelable slivers).
- Cells that do not sum to the stated whole, or a percentage column that doesn't total 100.
- Stripe layout instead of squarified — defeats area comparison.
- Rainbow fills: one hue per cell destroys the rank reading and the one-accent rule.
- Nesting more than two levels deep in a static diagram; a second level needs a heavier border and its own label tier, and a third is unreadable without interaction.
- 3-D or shadowed cells — area is already the message.
## Examples
- `assets/example-treemap.html` — minimal light
- `assets/example-treemap-dark.html` — minimal dark
- `assets/example-treemap-full.html` — full editorial