Indented tree
file/spec outline
A file or spec outline. The chart that looks like the thing it describes.
- Format.d3
- Length83 lines
- Includesnone
The source
83 lines of D3 JavaScript, and uses only the bundled D3 build. Copy it, or open the template inside Gnomon and render it as it is.
// Indented tree — a file explorer as a diagram. Every node gets its own
// row, so long names never collide and you can hang columns of metrics off
// the right-hand side.
const data = {
name: 'desktop/src', children: [
{ name: 'api', children: [
{ name: 'plantuml.ts', size: 312 },
{ name: 'board.ts', size: 96 },
{ name: 'chat.ts', size: 74 },
]},
{ name: 'stores', children: [
{ name: 'editorStore.ts', size: 1060 },
{ name: 'fileStore.ts', size: 430 },
{ name: 'plannerStore.ts', size: 388 },
]},
{ name: 'utils', children: [
{ name: 'hexagonal.ts', size: 340 },
{ name: 'd3Render.ts', size: 300 },
{ name: 'ea', children: [
{ name: 'parse.ts', size: 223 },
{ name: 'render.ts', size: 408 },
]},
]},
],
};
// `eachBefore` visits in document order, which is exactly the row order — so
// the index it hands the callback *is* the y position. No layout pass needed.
const root = d3.hierarchy(data).eachBefore((d, i) => { d.index = i; });
const rowHeight = 22;
const indent = 18;
const rows = root.descendants();
const boxHeight = rows.length * rowHeight + 30;
const svg = d3.select(container).append('svg')
.attr('viewBox', [0, 0, width, boxHeight])
.attr('width', width)
.attr('height', boxHeight)
.attr('font-family', 'system-ui, sans-serif')
.attr('font-size', 12);
const x = d => 14 + d.depth * indent;
const y = d => 20 + d.index * rowHeight;
svg.append('g')
.attr('fill', 'none')
.attr('stroke', theme.muted)
.attr('stroke-opacity', 0.45)
.selectAll('path')
.data(root.links())
.join('path')
.attr('d', d => `M${x(d.source)},${y(d.source)}V${y(d.target)}h${indent - 4}`);
const node = svg.append('g')
.selectAll('g')
.data(rows)
.join('g')
.attr('transform', d => `translate(${x(d)},${y(d)})`);
node.append('circle')
.attr('r', 3.5)
.attr('fill', d => d.children ? theme.accent : theme.muted);
node.append('text')
.attr('dy', '0.32em')
.attr('x', 8)
.attr('fill', theme.foreground)
.attr('font-weight', d => d.children ? 600 : 400)
.text(d => d.data.name);
// Right-hand metric column — the reason to pick this over a tidy tree.
svg.append('g')
.attr('text-anchor', 'end')
.attr('fill', theme.muted)
.selectAll('text')
.data(rows.filter(d => d.data.size))
.join('text')
.attr('x', width - 14)
.attr('y', d => y(d))
.attr('dy', '0.32em')
.text(d => `${d.data.size} lines`);Render this offline
This template ships in Gnomon and renders on your machine, with no account and nothing sent to a server. The browser editor is free and needs no install.
Others in D3 visualisations
- Sankey — request flowFlow with volume. The best chart here for "where does it all go".
- Chord — service interactionWho talks to whom, when the traffic is bidirectional.
- Arc diagram — dependenciesNodes on one axis, arcs above. Readable where a force graph is not, provided the ordering means something.
- Adjacency matrix — couplingAdjacency matrix. Unfashionable, and better than a force graph for dense dependencies.
- Edge bundling — module importsHierarchical edge bundling. For import graphs big enough that straight edges become a hairball.
- Sunburst — nested spendNested hierarchy, radially. Prettier than an icicle, harder to compare.
- Icicle — nested spend (linear)A sunburst unrolled flat. Harder to love, much easier to compare siblings.
- Circle packing — nested sizeCircle packing. Nested size when the nesting matters more than reading exact areas.
- Treemap — portfolio costNested size. Good for cost, storage, lines of code.
- Tidy tree — structureA tidy tree. The default for anything with one parent per node.
- Radial tree — structure (radial)The same tree bent into a circle. Fits more depth on a slide, costs you easy comparison.
- Dendrogram — clusteringClustering, with join height carrying the distance. Not merely a tree with curves.
- Force graph — service mapService map. Use for clusters, not for reading individual edges.
- Force graph — disjoint clustersForce graph that keeps unconnected clusters apart rather than flinging them off screen.
- Force graph — radial tiersForce layout pinned to rings, so tier is a position instead of a colour legend.
- Directed graph — call directionDirected edges with arrowheads. For when direction is the question, not just adjacency.
- Force tree — blast radiusBlast radius from one node: what breaks if this goes.
- Calendar heatmap — daily activityDaily activity over a year: deploys, incidents, commits.
- Streamgraph — shifting mixShifting composition over time. Good for the mix, poor for reading any single value.
- Gantt — delivery roadmapDelivery roadmap, rendered from data.
- Radar — capability scoringCapability scoring across axes. Fine for one subject, misleading with four overlaid.
- Bullet chart — target vs actualTarget against actual, in one line. The chart a gauge wishes it were.
- Beeswarm — distribution by groupEvery point, grouped, without the overplotting a strip plot suffers.
- Horizon chart — many series, little spaceMany series in little space. Takes a moment to learn, then very dense.
- Slope chart — before and afterBefore and after, two points, one line each. Devastatingly clear.
- Parallel coordinates — multi-criteriaMulti-criteria comparison: for option analysis.
- Box plot — latency distributionLatency distribution. The chart that shows the tail a mean hides.
- Grouped bar — category comparisonCategory comparison. Unglamorous, and usually the right answer.
- Multi-line — metrics over timeMetrics over time. Keep it under about five series, or switch to horizon.