Slope chart
before and after
Before and after, two points, one line each. Devastatingly clear.
- Format.d3
- Length81 lines
- Includesnone
The source
81 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.
// Slope chart — two points in time, one line each.
// The whole message is in the direction of the slopes, so colour by
// improvement/regression and let everything else recede.
const rows = [
{ name: 'Quote Engine', before: 82, after: 91 },
{ name: 'Broker Portal', before: 74, after: 79 },
{ name: 'Customer Portal', before: 68, after: 88 },
{ name: 'Contact Centre', before: 61, after: 44 },
{ name: 'Policy Admin Core', before: 39, after: 42 },
{ name: 'Legacy Ledger', before: 55, after: 31 },
{ name: 'Underwriting', before: 77, after: 84 },
{ name: 'Risk Scoring', before: 88, after: 93 },
{ name: 'Data Lakehouse', before: 71, after: 86 },
{ name: 'BI & Reporting', before: 64, after: 63 },
];
const margin = { top: 54, right: 190, bottom: 30, left: 190 };
const y = d3.scaleLinear()
.domain(d3.extent([...rows.map(d => d.before), ...rows.map(d => d.after)])).nice()
.range([height - margin.bottom, margin.top]);
const xLeft = margin.left;
const xRight = width - margin.right;
const up = theme.palette[4];
const down = theme.palette[2];
const flat = theme.muted;
const colourOf = d => {
const delta = d.after - d.before;
if (delta > 2) return up;
if (delta < -2) return down;
return flat;
};
const svg = d3.select(container).append('svg')
.attr('viewBox', [0, 0, width, height])
.attr('width', width)
.attr('height', height)
.attr('font-family', 'system-ui, sans-serif')
.attr('font-size', 11);
svg.append('g')
.attr('stroke-width', 2)
.selectAll('line')
.data(rows)
.join('line')
.attr('x1', xLeft).attr('y1', d => y(d.before))
.attr('x2', xRight).attr('y2', d => y(d.after))
.attr('stroke', colourOf)
.attr('stroke-opacity', 0.85);
for (const [x, key, anchor, dx] of [[xLeft, 'before', 'end', -10], [xRight, 'after', 'start', 10]]) {
const g = svg.append('g');
g.selectAll('circle')
.data(rows)
.join('circle')
.attr('cx', x)
.attr('cy', d => y(d[key]))
.attr('r', 4)
.attr('fill', colourOf);
g.selectAll('text')
.data(rows)
.join('text')
.attr('x', x + dx)
.attr('y', d => y(d[key]))
.attr('dy', '0.35em')
.attr('text-anchor', anchor)
.attr('fill', theme.foreground)
.text(d => `${d.name} ${d[key]}`);
}
svg.append('g')
.attr('fill', theme.muted)
.attr('font-weight', 600)
.selectAll('text')
.data([['2024 health', xLeft, 'end'], ['2026 health', xRight, 'start']])
.join('text')
.attr('x', d => d[1] + (d[2] === 'end' ? -10 : 10))
.attr('y', margin.top - 24)
.attr('text-anchor', d => d[2])
.text(d => d[0]);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.
- Indented tree — file/spec outlineA file or spec outline. The chart that looks like the thing it describes.
- 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.
- 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.