chart-factory 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +162 -0
- package/dist/chart-factory-table.js +1400 -0
- package/dist/chart-factory.css +2368 -0
- package/dist/chart-factory.js +11398 -0
- package/dist/chart-factory.min.js +7 -0
- package/docs/API.md +1815 -0
- package/docs/chart-guide.md +872 -0
- package/docs/color-guide.md +134 -0
- package/docs/conformance-matrix.md +864 -0
- package/docs/theming.md +107 -0
- package/index.d.ts +10453 -0
- package/package.json +83 -0
- package/src/core/base.css +508 -0
- package/src/core/builder-metadata.generated.mjs +406 -0
- package/src/core/d3-base.js +249 -0
- package/src/core/d3-chart-factory.js +18547 -0
- package/src/core/d3-data.js +0 -0
- package/src/core/d3-suggest.js +565 -0
- package/src/core/example-nav.css +62 -0
- package/src/core/example-nav.js +249 -0
- package/src/core/palettes.js +310 -0
- package/src/core/theme-toggle.js +126 -0
- package/src/core/tokens.css +792 -0
- package/src/table/d3-table.js +1570 -0
- package/src/table/table.css +899 -0
- package/src/table/tokens.css +157 -0
|
@@ -0,0 +1,872 @@
|
|
|
1
|
+
# Chart guide — which chart, for which data
|
|
2
|
+
|
|
3
|
+
> **Generated file — do not edit.** Per-builder entries come from
|
|
4
|
+
> `metadata/*.mjs` (`whenToUse` / `notFor` / `cardinality`; schema in
|
|
5
|
+
> [../metadata/README.md](../metadata/README.md)). Regenerate with
|
|
6
|
+
> `npm run conformance`.
|
|
7
|
+
|
|
8
|
+
This is the chart-**selection** layer: given your data's shape and the
|
|
9
|
+
question you're answering, which builder to reach for — and when a nearby
|
|
10
|
+
variant is the better call. Option-level support lives in the
|
|
11
|
+
[conformance matrix](conformance-matrix.md), full config in [API.md](API.md),
|
|
12
|
+
and color judgment in the [color guide](color-guide.md).
|
|
13
|
+
|
|
14
|
+
## Start from the question
|
|
15
|
+
|
|
16
|
+
| The question | Reach for | When it bends |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| How has one measure changed over time? | Line.createBasic (dense series: Line.createYearly) | Area.createBasic when filled magnitude-from-zero is the message |
|
|
19
|
+
| How do a few series' trends compare? | Line.createMulti | Past ~6 series: `facet()` or an interactive-legend isolate; same measure across years: Line.createYoY |
|
|
20
|
+
| How does the mix (part-of-whole) change over time? | Area.createStacked | Totals are noise: Area.createNormalized; few periods: Bar.createVerticalStacked; many series, gestalt only: Area.createStreamgraph |
|
|
21
|
+
| How do categories compare on one value? | Bar.createBasic | Ordered/time-ish categories: Bar.createVertical; past ~20 rows: `data.topN` or Table.createSortable |
|
|
22
|
+
| What's the composition per category? | Bar.createStacked | Individual key values matter more than totals: Bar.createGrouped |
|
|
23
|
+
| What changed between exactly two points in time? | Slope.createBasic | Past ~20 items, or gap size beats crossings: Dot.createDumbbell |
|
|
24
|
+
| Signed values around a meaningful zero? | Bar.createDiverging | Deltas that accumulate to a total: Bar.createWaterfall; over time: Line.createGrowthRate |
|
|
25
|
+
| How is one numeric distributed? | Histogram.createBasic | Individuals should stay visible: Scatter.createBeeswarm; countable small n: Dot.createUnitHistogram |
|
|
26
|
+
| How do distributions compare across groups? | BoxPlot.createBasic | ≤4 groups with shape detail: Histogram.createOverlay; individuals visible: Scatter.createBeeswarm |
|
|
27
|
+
| Are two numeric measures related? | Scatter.createBasic | Dense clouds: Scatter.createHexbin / createHeatmap / createContour; identity matters: Scatter.createLogos; same-scale verdict: Scatter.createDiagonal; strategic cells: Scatter.createQuadrant |
|
|
28
|
+
| Part-of-whole at one moment? | Donut.createBasic | More than ~6 slices or close values: Bar.createBasic |
|
|
29
|
+
| How did rankings shift over periods? | Line.createBumpChart | Magnitudes matter, not just order: Line.createMulti |
|
|
30
|
+
| What flows where? | Sankey.createBasic | — |
|
|
31
|
+
| What's the daily rhythm? | Calendar.createHeatmap | — |
|
|
32
|
+
| What happens when, for how long? | Bar.createGantt | — |
|
|
33
|
+
| KPI vs target? | Bar.createBullet | Attainment toward a fixed max: Bar.createProgress |
|
|
34
|
+
| How certain are these estimates? | Dot.createConfidence | Expected range over time: Line.createForecastBand; historical context: Dot.createPercentile |
|
|
35
|
+
| The reader needs exact values? | Table.createSortable | Add scannability: Table.createWithBars / Table.createWithHeatmap / Table.createCombined |
|
|
36
|
+
|
|
37
|
+
## Start from the data signature
|
|
38
|
+
|
|
39
|
+
The mechanical route: count your **dimensions** (categorical fields you'd
|
|
40
|
+
group by), note whether one is **temporal** (dates, ordered periods), and
|
|
41
|
+
count your **measures** (numeric fields you'd aggregate). Then look up the
|
|
42
|
+
signature. Where this table and the question table disagree, the question
|
|
43
|
+
wins — the signature narrows candidates, the question picks.
|
|
44
|
+
|
|
45
|
+
This table also runs at runtime: `ChartFactory.suggest(rows)` profiles the
|
|
46
|
+
fields, applies these rules scored against each builder's cardinality
|
|
47
|
+
ranges, and returns ranked candidates with render-ready configs (see
|
|
48
|
+
[API.md](API.md#suggest-chartfactorysuggest)).
|
|
49
|
+
|
|
50
|
+
| Signature | Reach for | When it bends |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| 1 measure, 0 dimensions (many records) | Histogram.createBasic | Individuals should stay visible: Scatter.createBeeswarmHorizontal; countable small n: Dot.createUnitHistogram |
|
|
53
|
+
| 1 categorical + 1 measure | Bar.createBasic (ranked) | Ordered categories: Bar.createVertical; the values are parts of one whole: Donut.createBasic |
|
|
54
|
+
| 1 categorical + 1 measure, many records per category | BoxPlot.createBasic | Individuals visible (<~150/category): Scatter.createBeeswarm; ≤4 groups with shape detail: Histogram.createOverlay |
|
|
55
|
+
| 1 temporal + 1 measure | Line.createBasic | Magnitude-from-zero: Area.createBasic; daily grain: Calendar.createHeatmap; exactly two dates: Slope.createBasic |
|
|
56
|
+
| 1 categorical + 1 temporal + 1 measure | Line.createMulti (series = the categorical) | >~6 categories: `facet()`; composition/mix: Area.createStacked; rank only: Line.createBumpChart; the categorical is "year" of the same measure: Line.createYoY |
|
|
57
|
+
| 2 categorical + 1 measure | Scatter.createHeatmap (categorical mode) | Both small (≤~6 × ≤~8): Bar.createGrouped / Bar.createStacked; exact-value lookup: Table.createWithHeatmap |
|
|
58
|
+
| 2 measures | Scatter.createBasic | Same scale/unit: Scatter.createDiagonal; a defensible center: Scatter.createQuadrant; add a categorical via colors/colorMap |
|
|
59
|
+
| 2 measures, same unit, paired per entity | Dot.createDumbbell | Crossings/rates matter and rows ≤~20: Slope.createBasic; it's an interval, not a pair: Bar.createRange |
|
|
60
|
+
| 3 measures | Scatter.createQuadrant with sizeAccessor | Put the two that must be read on position; size carries the third (read coarsely) |
|
|
61
|
+
| n same-unit measures + 1 categorical | Dot.createBasic | Different units: Table.createCombined — a shared axis forces one unit |
|
|
62
|
+
| 2 measures + temporal, per entity | Scatter.createTrajectory | Only two time points: Scatter.createComet |
|
|
63
|
+
| categorical → categorical + 1 measure (flow) | Sankey.createBasic | — |
|
|
64
|
+
| 1 categorical + temporal interval (start, end) | Bar.createGantt | — |
|
|
65
|
+
|
|
66
|
+
Signatures this library deliberately doesn't serve: **spatial** (maps) and
|
|
67
|
+
network graphs — application territory.
|
|
68
|
+
|
|
69
|
+
## When a dimension is temporal
|
|
70
|
+
|
|
71
|
+
Time changes the defaults. In order:
|
|
72
|
+
|
|
73
|
+
1. **Count the periods.** Exactly 2 → Slope.createBasic or Dot.createDumbbell.
|
|
74
|
+
A handful of discrete periods (3–30) → Bar.createVertical (columns are
|
|
75
|
+
legitimate for discrete time). Dense or continuous → Line/Area; never
|
|
76
|
+
horizontal bars for time.
|
|
77
|
+
2. **Time goes on x, left → right.** Pass `Date` objects or ISO strings and
|
|
78
|
+
trend charts switch to a time scale with adaptive tick formatting
|
|
79
|
+
automatically — don't pre-format dates into strings.
|
|
80
|
+
3. **Match the builder to the temporal question.** Same measure compared
|
|
81
|
+
across years (seasonality): Line.createYoY. Rank over time:
|
|
82
|
+
Line.createBumpChart. Composition over time: Area.createStacked /
|
|
83
|
+
createNormalized. Daily rhythm: Calendar.createHeatmap. Scheduled
|
|
84
|
+
intervals: Bar.createGantt. Actual → future with uncertainty:
|
|
85
|
+
Line.createDynamicBand; tracking against an expected range:
|
|
86
|
+
Line.createForecastBand. Signed change over time: Line.createGrowthRate.
|
|
87
|
+
4. **Mark events, don't add series.** Point-in-time context (launches,
|
|
88
|
+
policy changes) is an `{type: 'event'}` annotation below the x-axis,
|
|
89
|
+
not another line.
|
|
90
|
+
|
|
91
|
+
## Rules that trump chart choice
|
|
92
|
+
|
|
93
|
+
- **Cardinality first.** Every builder below lists comfortable ranges. When
|
|
94
|
+
the data exceeds them, don't shrink the marks — reshape the data
|
|
95
|
+
(`data.topN` with an auto-grayed "Other", `data.rollup` to a coarser
|
|
96
|
+
grain, `facet()` into small multiples) or move to a denser form.
|
|
97
|
+
- **Rank is not magnitude.** Bump charts and sorted bars show order; if the
|
|
98
|
+
reader will ask "by how much?", plot the values.
|
|
99
|
+
- **Shares need a whole.** Normalized/donut forms answer "what fraction?" —
|
|
100
|
+
if anyone will ask "how much total?", keep absolute values in view.
|
|
101
|
+
- **Density forms need density.** Hexbin/contour/KDE/heatmap summarize what a
|
|
102
|
+
scatter can't show; below their minimum n they invent structure that isn't
|
|
103
|
+
there. Small n is a feature — show the individuals.
|
|
104
|
+
- **Two time points is a special number.** Slope and dumbbell exist for it;
|
|
105
|
+
three or more points want a line.
|
|
106
|
+
- **When precision beats perception, use a table.** A chart the reader
|
|
107
|
+
squints at to extract numbers should have been a table row.
|
|
108
|
+
- **Spend the best encodings on the most important variables.** Perceptual
|
|
109
|
+
accuracy ranks roughly: position > length > slope/angle > area > color
|
|
110
|
+
intensity > hue (Cleveland & McGill; Mackinlay). This is why donuts cap at
|
|
111
|
+
~6 slices (angle), why heatmap/calendar values read coarsely (color), and
|
|
112
|
+
why a third measure goes on bubble size only after the two that must be
|
|
113
|
+
read exactly are on the axes.
|
|
114
|
+
|
|
115
|
+
## Grounding
|
|
116
|
+
|
|
117
|
+
The taxonomy here follows the field's standard references: the
|
|
118
|
+
[FT Visual Vocabulary](https://github.com/Financial-Times/chart-doctor/tree/main/visual-vocabulary)
|
|
119
|
+
(question table — its nine data relationships minus spatial),
|
|
120
|
+
Mackinlay's APT expressiveness/effectiveness criteria and the
|
|
121
|
+
[Draco](https://github.com/cmudig/draco2) constraint line (signature table,
|
|
122
|
+
encoding ranking), and Cleveland & McGill's graphical-perception experiments
|
|
123
|
+
(the accuracy ordering above).
|
|
124
|
+
|
|
125
|
+
## Data-shape cheat sheet
|
|
126
|
+
|
|
127
|
+
- **Tidy rows, one numeric over ordered x** `{ x, y }` → Line / Area basics.
|
|
128
|
+
- **Wide rows, one field per series** `{ x, key1..keyN }` → stacked/normalized/grouped builders (`keys: [...]`); get there with `data.pivotWider`.
|
|
129
|
+
- **Series-of-arrays** `series: [{ name, data: [{x, y}] }]` → Line.createMulti / createYoY / createTextures.
|
|
130
|
+
- **Category + value** `{ category, value }` → Bar family, BoxPlot (raw mode), beeswarms.
|
|
131
|
+
- **Two values per row** `{ name, before, after }` / `{ name, start, end }` / `{ name, low, high }` → Slope, Dot.createDumbbell, Bar.createRange (aliases are normalized — both spellings work).
|
|
132
|
+
- **Two numerics per record** `{ x, y }` → Scatter family.
|
|
133
|
+
- **Flows** `{ source, target, value }` → Sankey. **Daily** `{ date, value }` → Calendar.
|
|
134
|
+
|
|
135
|
+
Ranges below are **comfortable ranges, not hard limits** — the point where
|
|
136
|
+
the form starts to degrade, and the moment to reshape or switch.
|
|
137
|
+
|
|
138
|
+
## Area
|
|
139
|
+
|
|
140
|
+
### Area.createBasic
|
|
141
|
+
|
|
142
|
+
Single-series area with line overlay, optional data-point dots, and the shared hover-line tooltip.
|
|
143
|
+
|
|
144
|
+
- **Use when:** One measure over continuous/ordered x where the filled magnitude from zero is the message — revenue, traffic, cumulative totals.
|
|
145
|
+
- **Not for:** Values without a meaningful zero baseline, or comparing several series (the fill hides crossings) — use Line.createBasic / Line.createMulti.
|
|
146
|
+
- **Comfortable at:** exactly 1 series · 2–500 points
|
|
147
|
+
- **Record:** `{ x: number | Date | ISO string, y: number }`
|
|
148
|
+
|
|
149
|
+
### Area.createDiverging
|
|
150
|
+
|
|
151
|
+
Single series filled against a baseline — above in one color, below in another — with the line stroke switching color at the crossing and a solid baseline rule.
|
|
152
|
+
|
|
153
|
+
- **Use when:** One measure read as above/below a meaningful reference — a long-run average, a target, .500, zero — where the polarity of each stretch is the message (surplus/deficit, hitters’ years vs pitchers’ years).
|
|
154
|
+
- **Not for:** Magnitude-from-zero stories (use Area.createBasic), period-over-period growth rates in percent (Line.createGrowthRate owns that framing), or categorical above/below comparisons (Bar.createDiverging).
|
|
155
|
+
- **Comfortable at:** exactly 1 series · 2–500 points
|
|
156
|
+
- **Record:** `{ x: number | Date | ISO string, y: number }`
|
|
157
|
+
|
|
158
|
+
### Area.createNormalized
|
|
159
|
+
|
|
160
|
+
100% stacked area (stackOffsetExpand, y fixed 0-1) with hover-line tooltip showing per-key percentages.
|
|
161
|
+
|
|
162
|
+
- **Use when:** Share-of-total over time when the changing mix is the message and the absolute total is noise — market share, channel mix.
|
|
163
|
+
- **Not for:** When the total matters (the 100% ceiling hides growth/shrinkage — use Area.createStacked), or very few time points (use Bar.createVerticalNormalized).
|
|
164
|
+
- **Comfortable at:** 2–6 series · 3–200 points
|
|
165
|
+
- **Record:** `{ x: number | Date | ISO string, key1..keyN: number (one field per entry in keys) }`
|
|
166
|
+
|
|
167
|
+
### Area.createSmoothed
|
|
168
|
+
|
|
169
|
+
Single-series smoothed (cardinal curve) area with vertical gradient fill, optional dots, and the shared hover-line tooltip.
|
|
170
|
+
|
|
171
|
+
- **Use when:** A single noisy series where the gestalt trend matters more than exact values — the cardinal curve visibly departs from the data points.
|
|
172
|
+
- **Not for:** Precise value reading or sparse data — smoothing invents curvature between points; use Area.createBasic. For honest smoothing, Line.createCombined shows a real moving average.
|
|
173
|
+
- **Comfortable at:** exactly 1 series · 10–500 points
|
|
174
|
+
- **Record:** `{ x: number | Date | ISO string, y: number }`
|
|
175
|
+
|
|
176
|
+
### Area.createStacked
|
|
177
|
+
|
|
178
|
+
Stacked multi-series area with per-key lines and a hover-line tooltip (per-key rows + optional Total footer).
|
|
179
|
+
|
|
180
|
+
- **Use when:** Part-of-whole composition over time when the total AND the mix both matter — the top edge is the total, layers are the mix.
|
|
181
|
+
- **Not for:** Reading individual series precisely — middle layers have no stable baseline; use Line.createMulti or facet(). Negative values do not stack.
|
|
182
|
+
- **Comfortable at:** 2–6 series · 3–200 points — Past ~6 keys, aggregate the tail with data.topN — a residual "Other" auto-grays.
|
|
183
|
+
- **Record:** `{ x: number | Date | ISO string, key1..keyN: number (one field per entry in keys) }`
|
|
184
|
+
|
|
185
|
+
### Area.createStreamgraph
|
|
186
|
+
|
|
187
|
+
Wiggle-offset streamgraph (curveBasis) with smart inline layer labels, hover-line tooltip with share bars, and a highlightKey extra.
|
|
188
|
+
|
|
189
|
+
- **Use when:** Many-series composition where organic flow and relative rise/fall are the point (genres over decades) — inline labels and highlightKey manage the crowd.
|
|
190
|
+
- **Not for:** Reading values or totals — the wiggle baseline sacrifices both; use Area.createStacked when anyone will ask "how much?".
|
|
191
|
+
- **Comfortable at:** 4–12 series · 10–200 points
|
|
192
|
+
- **Record:** `{ x: number | Date | ISO string, key1..keyN: number (one field per entry in keys) }`
|
|
193
|
+
|
|
194
|
+
## Line
|
|
195
|
+
|
|
196
|
+
### Line.createAreaGradient
|
|
197
|
+
|
|
198
|
+
Single line over a vertical-gradient area fill, hover-line tooltip.
|
|
199
|
+
|
|
200
|
+
- **Use when:** A single series that needs visual weight without a hard area fill — hero/featured chart styling for the same data Line.createBasic would show.
|
|
201
|
+
- **Not for:** Multi-series comparison, or when the gradient would imply a magnitude-from-zero reading the data cannot support.
|
|
202
|
+
- **Comfortable at:** exactly 1 series · 2–1000 points
|
|
203
|
+
- **Record:** `{ x: number | Date | ISO string, y: number }`
|
|
204
|
+
|
|
205
|
+
### Line.createBasic
|
|
206
|
+
|
|
207
|
+
Single line with optional data-point dots and the shared hover-line tooltip.
|
|
208
|
+
|
|
209
|
+
- **Use when:** The default for one measure over ordered x (usually time) where level and change both need precise reading.
|
|
210
|
+
- **Not for:** Categorical x with no order (Bar.createVertical) or several series (Line.createMulti).
|
|
211
|
+
- **Comfortable at:** exactly 1 series · 2–1000 points
|
|
212
|
+
- **Record:** `{ x: number | Date | ISO string, y: number }`
|
|
213
|
+
|
|
214
|
+
### Line.createBumpChart
|
|
215
|
+
|
|
216
|
+
Ranking changes over periods: point-scale x of period labels, inverted rank y, auto-measured side label margins, staggered draw-in animation.
|
|
217
|
+
|
|
218
|
+
- **Use when:** Rank order changing across a handful of periods — who passed whom; entries and exits (churn) are supported.
|
|
219
|
+
- **Not for:** Magnitudes — rank spacing is uniform, so a 1-vs-2 gap can hide a 10x value gap; use Line.createMulti on the values when "by how much" matters.
|
|
220
|
+
- **Comfortable at:** 2–10 series · 2–12 periods
|
|
221
|
+
- **Record:** `{ name: string, rankings: [{ period: 'string', rank: 'number' }] (need not cover every period — churn supported) }`
|
|
222
|
+
|
|
223
|
+
### Line.createCombined
|
|
224
|
+
|
|
225
|
+
Muted raw line + computed trailing moving-average overlay, dual-dot hover tooltip.
|
|
226
|
+
|
|
227
|
+
- **Use when:** One noisy series shown with both the raw signal (muted) and its computed trailing moving average — smoothing without lying about the data.
|
|
228
|
+
- **Not for:** Comparing different measures (Line.createMulti) or pre-computed smoothed columns (Line.createCumulativeVsMoving).
|
|
229
|
+
- **Comfortable at:** exactly 1 series · 10–1000 points
|
|
230
|
+
- **Record:** `{ x: number | Date | ISO string, y: number }`
|
|
231
|
+
|
|
232
|
+
### Line.createCumulativeVsMoving
|
|
233
|
+
|
|
234
|
+
Pre-computed cumulative vs moving-average lines with hot/cold gradient shading between them.
|
|
235
|
+
|
|
236
|
+
- **Use when:** Two precomputed views of one series — cumulative vs moving average — with hot/cold shading where they diverge (pace vs season average).
|
|
237
|
+
- **Not for:** Raw rows (both columns must be precomputed) or generic two-series comparison — use Line.createMulti.
|
|
238
|
+
- **Comfortable at:** exactly 2 series · 10–1000 points
|
|
239
|
+
- **Record:** `{ x: number | Date | ISO string, cumulative: number, moving: number }`
|
|
240
|
+
|
|
241
|
+
### Line.createDynamicBand
|
|
242
|
+
|
|
243
|
+
Actual → forecast handoff: solid actual line, dashed forecast line, expanding band, optional divider at forecastStart.
|
|
244
|
+
|
|
245
|
+
- **Use when:** Actual history handing off to a forecast at a known date — solid turns dashed with an expanding uncertainty band.
|
|
246
|
+
- **Not for:** Retrospective anomaly detection over a fully-actual series — use Line.createForecastBand or createPulseAnomalies.
|
|
247
|
+
- **Comfortable at:** exactly 1 series · 10–1000 points
|
|
248
|
+
- **Record:** `{ x: number | Date | ISO string, actual: number | null, forecast: number | null, upper: number, lower: number }`
|
|
249
|
+
|
|
250
|
+
### Line.createForecastBand
|
|
251
|
+
|
|
252
|
+
Actual line inside a confidence band with dashed anomaly markers where the actual exits the band.
|
|
253
|
+
|
|
254
|
+
- **Use when:** An actual measure tracked against a precomputed expected range, with anomaly markers where it exits the band — monitoring and backtests.
|
|
255
|
+
- **Not for:** An actual→forecast handoff at a known date (Line.createDynamicBand). The builder draws bands you supply; it does not fit models.
|
|
256
|
+
- **Comfortable at:** exactly 1 series · 10–2000 points
|
|
257
|
+
- **Record:** `{ x: number | Date | ISO string, actual: number, upper: number, lower: number }`
|
|
258
|
+
|
|
259
|
+
### Line.createGrowthRate
|
|
260
|
+
|
|
261
|
+
Positive/negative colored line + gradient areas around a zero baseline (symmetric y domain, clip-path split).
|
|
262
|
+
|
|
263
|
+
- **Use when:** A signed rate oscillating around zero (growth %, net change) — color flips at the zero baseline and the y domain is symmetric.
|
|
264
|
+
- **Not for:** All-positive levels — the symmetric zero-centered domain wastes half the plot; use Line.createBasic.
|
|
265
|
+
- **Comfortable at:** exactly 1 series · 3–500 points
|
|
266
|
+
- **Record:** `{ x: number | Date | ISO string, value: number (± growth rate) }`
|
|
267
|
+
|
|
268
|
+
### Line.createMulti
|
|
269
|
+
|
|
270
|
+
Multiple lines on one x axis with a shared hover-line tooltip (all series at nearest x).
|
|
271
|
+
|
|
272
|
+
- **Use when:** Comparing the trends of a few series on one axis — crossings and relative slopes are the message.
|
|
273
|
+
- **Not for:** More than ~6 lines (spaghetti — use facet(), an interactive-legend isolate, or Line.createBumpChart if only rank order matters).
|
|
274
|
+
- **Comfortable at:** 2–6 series · 2–500 points/series
|
|
275
|
+
- **Record:** series: [{ name?, data: [{ x, y }], color?, yAccessor?, valueFormat?, width?, opacity?, front? }]
|
|
276
|
+
|
|
277
|
+
### Line.createPulseAnomalies
|
|
278
|
+
|
|
279
|
+
Band chart whose anomalous points get pulsing dots — flagged via anomalyAccessor or auto-detected as out-of-band.
|
|
280
|
+
|
|
281
|
+
- **Use when:** Live-monitoring feel: a band chart whose anomalous points pulse — flagged via anomalyAccessor or auto-detected as out-of-band.
|
|
282
|
+
- **Not for:** Static or print output (the pulse is the point) and series with many anomalies — a screen full of pulsing dots reads as alarm, not information.
|
|
283
|
+
- **Comfortable at:** exactly 1 series · 10–1000 points
|
|
284
|
+
- **Record:** `{ x: number | Date | ISO string, actual: number, upper: number, lower: number }`
|
|
285
|
+
|
|
286
|
+
### Line.createSmoothed
|
|
287
|
+
|
|
288
|
+
Alias: delegates to Line.createYearly with curve FORCED to d3.curveBasis (L1779-1784) — a user-passed curve is silently overridden.
|
|
289
|
+
|
|
290
|
+
- **Use when:** The createYearly dense line with forced curveBasis smoothing — noisy dense series where shape beats fidelity.
|
|
291
|
+
- **Not for:** Anything needing faithful values — the curve is forced (a user-passed curve is overridden) and departs from the data; use Line.createYearly, or Line.createCombined for an honest moving average.
|
|
292
|
+
- **Comfortable at:** exactly 1 series · 30–5000 points
|
|
293
|
+
- **Record:** `{ x: number | Date | ISO string, y: number }`
|
|
294
|
+
|
|
295
|
+
### Line.createTextures
|
|
296
|
+
|
|
297
|
+
Multi-line chart distinguishing series by dash texture rather than a rotating palette.
|
|
298
|
+
|
|
299
|
+
- **Use when:** A few series that must stay distinguishable without color — print, single-hue brands, or colorblind-critical contexts (dash textures carry identity).
|
|
300
|
+
- **Not for:** More than ~4 series (dash vocabularies exhaust fast) or series with unaligned point indexes — hover assumes aligned indexes across series.
|
|
301
|
+
- **Comfortable at:** 2–4 series · 2–300 points/series
|
|
302
|
+
- **Record:** series: [{ name?, data, color?, dash?, xAccessor?, yAccessor? }] — dash is an SVG dasharray string ('' = solid); hover assumes ALIGNED point indexes across series (L2185, L2198)
|
|
303
|
+
|
|
304
|
+
### Line.createYearly
|
|
305
|
+
|
|
306
|
+
Dense single-series time-series line, no dots, hover-line tooltip.
|
|
307
|
+
|
|
308
|
+
- **Use when:** A long, dense single series (daily/weekly points over months or years) — no dots, built for point counts where markers would smear.
|
|
309
|
+
- **Not for:** Short series where individual points deserve markers — use Line.createBasic.
|
|
310
|
+
- **Comfortable at:** exactly 1 series · 50–5000 points
|
|
311
|
+
- **Record:** `{ x: number | Date | ISO string, y: number }`
|
|
312
|
+
|
|
313
|
+
### Line.createYoY
|
|
314
|
+
|
|
315
|
+
Year-over-year overlay: multiple series on one axis with an emphasis palette (oldest gray → newest blue).
|
|
316
|
+
|
|
317
|
+
- **Use when:** The same measure compared across years — one series per year with a recency ramp (oldest gray, newest blue); a partial current year is safe.
|
|
318
|
+
- **Not for:** Series that are not the same measure repeated over periods — the emphasis palette implies recency; use Line.createMulti.
|
|
319
|
+
- **Comfortable at:** 2–6 series · 2–400 points/series
|
|
320
|
+
- **Record:** series: [{ name?, data, color?, xAccessor?, yAccessor?, valueFormat?, width?, opacity?, front? }] — one entry per year; hover bisects each series independently (shared addMultiHoverTooltip), so unequal point counts (e.g. a partial current season) are safe
|
|
321
|
+
|
|
322
|
+
## Slope
|
|
323
|
+
|
|
324
|
+
### Slope.createAnimated
|
|
325
|
+
|
|
326
|
+
Slope chart with staggered line-draw animation.
|
|
327
|
+
|
|
328
|
+
- **Use when:** The two-snapshot slope with staggered draw-in — presentations and scrollytelling where the reveal is part of the story.
|
|
329
|
+
- **Not for:** Dashboards and static/print contexts — the animation replays add nothing after the first view; use createBasic.
|
|
330
|
+
- **Comfortable at:** 2–20 rows · exactly 2 periods
|
|
331
|
+
- **Record:** `{ name: string, before: number, after: number }`
|
|
332
|
+
|
|
333
|
+
### Slope.createBands
|
|
334
|
+
|
|
335
|
+
Slope chart over background band zones (default: four quartile bands, yDomain [0, 100]).
|
|
336
|
+
|
|
337
|
+
- **Use when:** Two-point change read against fixed reference zones (quartiles, grade bands) — did items change tier, not just value?
|
|
338
|
+
- **Not for:** Data without meaningful fixed zones — bands imply thresholds; use createBasic with yDomain instead.
|
|
339
|
+
- **Comfortable at:** 2–20 rows · exactly 2 periods
|
|
340
|
+
- **Record:** `{ name: string, before: number, after: number }`
|
|
341
|
+
|
|
342
|
+
### Slope.createBasic
|
|
343
|
+
|
|
344
|
+
Two-column slope chart (before/after) with value/name labels and per-line tooltips.
|
|
345
|
+
|
|
346
|
+
- **Use when:** Exactly two snapshots (before/after) across many items — every item’s direction and rate of change in one glance.
|
|
347
|
+
- **Not for:** More than two periods (Line.createMulti or createBumpChart) or more than ~20 items (labels collide — use Dot.createDumbbell rows).
|
|
348
|
+
- **Comfortable at:** 2–20 rows · exactly 2 periods
|
|
349
|
+
- **Record:** `{ name: string, before: number, after: number }`
|
|
350
|
+
|
|
351
|
+
### Slope.createCategorical
|
|
352
|
+
|
|
353
|
+
Slope chart with one d3.schemeTableau10 color per line.
|
|
354
|
+
|
|
355
|
+
- **Use when:** The two-snapshot slope when items belong to groups worth coloring — one hue per line from a categorical scheme.
|
|
356
|
+
- **Not for:** Many items — per-item colors stop being readable past ~10 lines; use createTrend or createHighlight to encode less.
|
|
357
|
+
- **Comfortable at:** 2–10 rows · exactly 2 periods
|
|
358
|
+
- **Record:** `{ name: string, before: number, after: number }`
|
|
359
|
+
|
|
360
|
+
### Slope.createHighlight
|
|
361
|
+
|
|
362
|
+
Slope chart with one item highlighted (orange) and the rest muted.
|
|
363
|
+
|
|
364
|
+
- **Use when:** One item’s two-point change against a deliberately muted field — "look at this one" framing.
|
|
365
|
+
- **Not for:** Comparing many items against each other — the muting hides them by design; use createTrend.
|
|
366
|
+
- **Comfortable at:** 3–25 rows · exactly 2 periods
|
|
367
|
+
- **Record:** `{ name: string, before: number, after: number }`
|
|
368
|
+
|
|
369
|
+
### Slope.createIndexed
|
|
370
|
+
|
|
371
|
+
Indexed "fan" slope chart: every item starts from one merged origin dot (all before values equal, e.g. indexed to 100) with a baseline hairline at the origin and stacked two-line right labels showing each item's signed percent change.
|
|
372
|
+
|
|
373
|
+
- **Use when:** Several measures indexed to a common base ("1995 = 100") — which grew, which shrank, and by how much, read as a fan of diverging lines from one origin.
|
|
374
|
+
- **Not for:** Items with genuinely different start values (Slope.createBasic keeps two real columns) or more than two periods (Line.createMulti indexed series).
|
|
375
|
+
- **Comfortable at:** 2–12 rows · exactly 2 periods
|
|
376
|
+
- **Record:** `{ name: string, before: number, after: number }`
|
|
377
|
+
|
|
378
|
+
### Slope.createTrend
|
|
379
|
+
|
|
380
|
+
Slope chart with trend coloring (up green, down red, flat gray).
|
|
381
|
+
|
|
382
|
+
- **Use when:** The two-snapshot slope when direction is the story — up/down/flat coloring sorts winners from losers at a glance.
|
|
383
|
+
- **Not for:** Data where group membership matters more than direction (Slope.createCategorical) or >2 periods (Line.createMulti).
|
|
384
|
+
- **Comfortable at:** 2–20 rows · exactly 2 periods
|
|
385
|
+
- **Record:** `{ name: string, before: number, after: number }`
|
|
386
|
+
|
|
387
|
+
## Bar
|
|
388
|
+
|
|
389
|
+
### Bar.createBasic
|
|
390
|
+
|
|
391
|
+
Horizontal bars with auto-measured category labels left and value labels at bar ends.
|
|
392
|
+
|
|
393
|
+
- **Use when:** The default categorical comparison — one value per category as ranked horizontal rows; long names fit naturally.
|
|
394
|
+
- **Not for:** More than ~20 rows (aggregate with data.topN or use Table.createSortable) or time-ordered categories (Bar.createVertical / Line).
|
|
395
|
+
- **Comfortable at:** 2–20 rows
|
|
396
|
+
- **Record:** `{ category: string, value: number }`
|
|
397
|
+
|
|
398
|
+
### Bar.createBullet
|
|
399
|
+
|
|
400
|
+
Bullet chart: slim measure bar, target tick, and optional qualitative range bands per row.
|
|
401
|
+
|
|
402
|
+
- **Use when:** KPI vs target with qualitative context bands — dashboard rows where each bar is a different metric, not a category.
|
|
403
|
+
- **Not for:** Plain category comparison without targets (Bar.createBasic) or many rows — bullets reward close reading.
|
|
404
|
+
- **Comfortable at:** 1–8 rows
|
|
405
|
+
- **Record:** `{ category: string, value: number, target: number, ranges: number[]? }`
|
|
406
|
+
|
|
407
|
+
### Bar.createDiverging
|
|
408
|
+
|
|
409
|
+
Bars diverging left (negative) / right (positive) from a centered category-label column.
|
|
410
|
+
|
|
411
|
+
- **Use when:** Signed values around a meaningful zero — surplus/deficit, above/below average, net sentiment.
|
|
412
|
+
- **Not for:** All-positive data (plain bars) or signed deltas that accumulate into a total (Bar.createWaterfall).
|
|
413
|
+
- **Comfortable at:** 2–20 rows
|
|
414
|
+
- **Record:** `{ category: string, value: number (may be negative) }`
|
|
415
|
+
|
|
416
|
+
### Bar.createGantt
|
|
417
|
+
|
|
418
|
+
Timeline (Gantt) bars spanning start-end ranges with a bottom time/linear axis, grid, and optional milestones.
|
|
419
|
+
|
|
420
|
+
- **Use when:** Activities spanning start→end on a shared timeline, with optional milestones — schedules, project phases, eras.
|
|
421
|
+
- **Not for:** Value magnitudes (bar length means duration here, not amount) or dependency graphs — dependencies are app territory.
|
|
422
|
+
- **Comfortable at:** 2–25 rows
|
|
423
|
+
- **Record:** `{ label: string, start: Date | number, end: Date | number }`
|
|
424
|
+
|
|
425
|
+
### Bar.createGrouped
|
|
426
|
+
|
|
427
|
+
Grouped horizontal bars (one sub-bar per key per category) with optional progress-style tracks.
|
|
428
|
+
|
|
429
|
+
- **Use when:** A few keys per category compared side by side with a shared baseline — when individual key values matter more than totals.
|
|
430
|
+
- **Not for:** More than ~4 keys (row count explodes) or when totals are the message (Bar.createStacked).
|
|
431
|
+
- **Comfortable at:** 2–8 rows · 2–4 series
|
|
432
|
+
- **Record:** `{ category: string, <key>: number — one field per entry in keys }`
|
|
433
|
+
|
|
434
|
+
### Bar.createInline
|
|
435
|
+
|
|
436
|
+
Horizontal bars with the category label inside the bar (white text) and value at the bar end.
|
|
437
|
+
|
|
438
|
+
- **Use when:** Basic horizontal bars in tight layouts — the category label sits inside the bar, saving the label column.
|
|
439
|
+
- **Not for:** Short bars or long names (labels overflow the bar) and values near zero — use Bar.createBasic.
|
|
440
|
+
- **Comfortable at:** 2–15 rows
|
|
441
|
+
- **Record:** `{ category: string, value: number }`
|
|
442
|
+
|
|
443
|
+
### Bar.createLabelAbove
|
|
444
|
+
|
|
445
|
+
Horizontal bars with the category label above each bar (taller rows, suits long names).
|
|
446
|
+
|
|
447
|
+
- **Use when:** Categorical bars with long labels — the name above each bar frees the full width for the bar itself.
|
|
448
|
+
- **Not for:** Many rows — the taller two-line rows roughly double vertical cost; past ~12 rows use Bar.createBasic.
|
|
449
|
+
- **Comfortable at:** 2–12 rows
|
|
450
|
+
- **Record:** `{ category: string, value: number }`
|
|
451
|
+
|
|
452
|
+
### Bar.createLikert
|
|
453
|
+
|
|
454
|
+
Diverging stacked (Likert) bars centered on neutral; doubles as a population pyramid, with a frame-tween update().
|
|
455
|
+
|
|
456
|
+
- **Use when:** Ordinal survey scales centered on neutral — agreement distributions per question, negative left / positive right.
|
|
457
|
+
- **Not for:** Keys without a negative→positive order — the diverging center assumes ordinal keys; use Bar.createStacked.
|
|
458
|
+
- **Comfortable at:** 1–12 rows · 3–7 series
|
|
459
|
+
- **Record:** `{ question: string, <key>: number — one field per entry in keys (ordered negative -> positive) }`
|
|
460
|
+
|
|
461
|
+
### Bar.createProgress
|
|
462
|
+
|
|
463
|
+
Progress-style bars against a fixed maximum with a background track and value-based coloring.
|
|
464
|
+
|
|
465
|
+
- **Use when:** Attainment toward a known maximum per item — completion, quota, capacity — with the remaining headroom visible as track.
|
|
466
|
+
- **Not for:** Open-ended values with no defensible maximum — use Bar.createBasic.
|
|
467
|
+
- **Comfortable at:** 1–15 rows
|
|
468
|
+
- **Record:** `{ category: string, value: number (0..maxValue) }`
|
|
469
|
+
|
|
470
|
+
### Bar.createPyramid
|
|
471
|
+
|
|
472
|
+
Population pyramid: two opposed magnitudes per category row mirrored around a center axis, with a frame-tween update().
|
|
473
|
+
|
|
474
|
+
- **Use when:** Two opposed populations mirrored per category band — the classic age pyramid; shape asymmetry is the message.
|
|
475
|
+
- **Not for:** More than two groups (Bar.createGrouped) or precise left-vs-right comparison — mirrored axes read badly for exact gaps; use Dot.createDumbbell.
|
|
476
|
+
- **Comfortable at:** 3–25 rows · exactly 2 series
|
|
477
|
+
- **Record:** `{ category: string, left: number — left-wing magnitude (field name configurable via leftKey), right: number — right-wing magnitude (field name configurable via rightKey) }`
|
|
478
|
+
|
|
479
|
+
### Bar.createRange
|
|
480
|
+
|
|
481
|
+
Horizontal floating bars spanning [low, high] per category on a numeric value axis (temperature ranges, salary bands).
|
|
482
|
+
|
|
483
|
+
- **Use when:** A low→high interval per category — temperature ranges, salary bands — where the extent is the message, not a point value.
|
|
484
|
+
- **Not for:** Point estimates with uncertainty (Dot.createConfidence) or before/after pairs where direction matters (Dot.createDumbbell).
|
|
485
|
+
- **Comfortable at:** 2–20 rows
|
|
486
|
+
- **Record:** `{ name: string, low: number, high: number }`
|
|
487
|
+
|
|
488
|
+
### Bar.createStacked
|
|
489
|
+
|
|
490
|
+
Horizontal stacked bars (one segment per series key) with rounded-outer-edge clipping and total labels.
|
|
491
|
+
|
|
492
|
+
- **Use when:** Composition per category — segment mix across a modest number of keys, with totals labeled at bar ends.
|
|
493
|
+
- **Not for:** Comparing middle segments across rows — only the first segment has a shared baseline; use Bar.createGrouped or Dot.createBasic.
|
|
494
|
+
- **Comfortable at:** 2–15 rows · 2–6 series
|
|
495
|
+
- **Record:** `{ category: string, <key>: number — one field per entry in keys }`
|
|
496
|
+
|
|
497
|
+
### Bar.createVertical
|
|
498
|
+
|
|
499
|
+
Vertical (column) bars with band x axis, linear y axis, grid, and optional labels above bars.
|
|
500
|
+
|
|
501
|
+
- **Use when:** Categorical comparison where x order carries meaning (months, ordered stages) or column form is expected; density-aware labels handle many bands.
|
|
502
|
+
- **Not for:** Long category names (horizontal bars read better) or truly continuous x (Line/Area).
|
|
503
|
+
- **Comfortable at:** 2–60 categories — Past ~25 bands labels auto-thin to round numbers — fine for time-ish x, wrong for names the reader must see; switch to horizontal bars.
|
|
504
|
+
- **Record:** `{ x: string (category), y: number }`
|
|
505
|
+
|
|
506
|
+
### Bar.createVerticalNormalized
|
|
507
|
+
|
|
508
|
+
100% stacked vertical columns — each column normalized to percentages of its own total.
|
|
509
|
+
|
|
510
|
+
- **Use when:** Share-of-total per ordered category (100% columns) — mix shift across periods when totals are noise.
|
|
511
|
+
- **Not for:** When totals matter (createVerticalStacked) — the 100% ceiling hides growth; single-series data.
|
|
512
|
+
- **Comfortable at:** 2–30 categories · 2–6 series
|
|
513
|
+
- **Record:** `{ x: string (category), <key>: number — one field per entry in keys (normalized to % of the row total) }`
|
|
514
|
+
|
|
515
|
+
### Bar.createVerticalStacked
|
|
516
|
+
|
|
517
|
+
Vertical stacked columns with rounded-top clipping, layer-by-layer build animation, and segment labels.
|
|
518
|
+
|
|
519
|
+
- **Use when:** Composition per ordered category — mix plus total per column, few periods (use Area.createStacked when x is dense enough to read as continuous).
|
|
520
|
+
- **Not for:** More than ~6 keys, or comparing middle segments across columns (no shared baseline — Bar.createGrouped or Line.createMulti).
|
|
521
|
+
- **Comfortable at:** 2–30 categories · 2–6 series
|
|
522
|
+
- **Record:** `{ x: string (category), <key>: number — one field per entry in keys }`
|
|
523
|
+
|
|
524
|
+
### Bar.createWaterfall
|
|
525
|
+
|
|
526
|
+
Waterfall: signed deltas become cumulative floating bars with sign-based coloring and connector hairlines; total rows re-anchor at zero.
|
|
527
|
+
|
|
528
|
+
- **Use when:** Signed steps composing a total — how you got from A to B (P&L bridges, headcount changes); total rows re-anchor at zero.
|
|
529
|
+
- **Not for:** Independent signed values that do not accumulate (Bar.createDiverging) or long sequences — past ~15 steps the bridge stops reading.
|
|
530
|
+
- **Comfortable at:** 3–15 steps
|
|
531
|
+
- **Record:** `{ name: string, value: number? — signed delta; ignored on total rows, total: boolean? — true renders the running total from zero (subtotal / grand total) }`
|
|
532
|
+
|
|
533
|
+
## Scatter
|
|
534
|
+
|
|
535
|
+
### Scatter.createAnimated
|
|
536
|
+
|
|
537
|
+
createBasic plus a continuous random-walk animation loop with fading gradient trails (demo/ambient — positions leave the data).
|
|
538
|
+
|
|
539
|
+
- **Use when:** Ambient/demo motion — a continuous random-walk loop with fading trails for hero sections and loading screens.
|
|
540
|
+
- **Not for:** Any analytical reading — positions leave the data after the first frame; for real movement over time use Scatter.createTrajectory.
|
|
541
|
+
- **Comfortable at:** 5–100 points
|
|
542
|
+
- **Record:** `{ x: number, y: number, category: string?, name: string? }`
|
|
543
|
+
|
|
544
|
+
### Scatter.createBasic
|
|
545
|
+
|
|
546
|
+
X/Y scatter with optional category coloring and DOT_STYLES presets.
|
|
547
|
+
|
|
548
|
+
- **Use when:** The relationship between two numeric measures across records — correlation, clusters, outliers.
|
|
549
|
+
- **Not for:** Dense clouds where dots occlude (≥~500 points — Scatter.createHexbin/createHeatmap/createContour) or one numeric across categories (beeswarm).
|
|
550
|
+
- **Comfortable at:** 10–1000 points
|
|
551
|
+
- **Record:** `{ x: number, y: number, category: string?, name: string? }`
|
|
552
|
+
|
|
553
|
+
### Scatter.createBeeswarm
|
|
554
|
+
|
|
555
|
+
Horizontal beeswarm grouped into category rows: value pins x exactly, dots dodge along y (deterministic beeswarmDodge, zero overlap).
|
|
556
|
+
|
|
557
|
+
- **Use when:** Distribution of one numeric across category rows with every individual visible and its value exact (dodge never distorts value).
|
|
558
|
+
- **Not for:** Large n — the dodge runs out of room around ~150 points per category; use BoxPlot.createBasic or Histogram. Two numerics want a scatter.
|
|
559
|
+
- **Comfortable at:** 1–8 categories · 10–400 points — ~150 points per category row before the swarm outgrows its band.
|
|
560
|
+
- **Record:** `{ value: number, category: string }`
|
|
561
|
+
|
|
562
|
+
### Scatter.createBeeswarmHorizontal
|
|
563
|
+
|
|
564
|
+
Single-row horizontal beeswarm (value on x, dodge around the vertical center); renders logos when imageAccessor is given, dots otherwise.
|
|
565
|
+
|
|
566
|
+
- **Use when:** A single-row horizontal swarm of one numeric — compact strip-plot framing; renders logos automatically when imageAccessor is given.
|
|
567
|
+
- **Not for:** Multiple category rows (createBeeswarm) or large n.
|
|
568
|
+
- **Comfortable at:** 5–150 points
|
|
569
|
+
- **Record:** `{ value: number, category: string?, name: string?, logo: string? }`
|
|
570
|
+
|
|
571
|
+
### Scatter.createBeeswarmLogos
|
|
572
|
+
|
|
573
|
+
Vertical single-column beeswarm of images: value pins y exactly, logos dodge along x around the center.
|
|
574
|
+
|
|
575
|
+
- **Use when:** A vertical swarm of images — small leagues/rosters where each mark must be recognizable.
|
|
576
|
+
- **Not for:** More than ~50 images (logo size vs collision radius stops working) or anonymous data — use createBeeswarmVertical dots.
|
|
577
|
+
- **Comfortable at:** 5–50 points
|
|
578
|
+
- **Record:** `{ value: number, image | logo: string, name: string? }`
|
|
579
|
+
|
|
580
|
+
### Scatter.createBeeswarmVertical
|
|
581
|
+
|
|
582
|
+
Vertical beeswarm (value on y), optional single centered column, per-item colors and in-dot text labels.
|
|
583
|
+
|
|
584
|
+
- **Use when:** One numeric distribution as a vertical swarm — league-table style with per-item colors and in-dot labels; singleColumn for one ungrouped field.
|
|
585
|
+
- **Not for:** Large n (~150 per column max) or when identity needs images — use createBeeswarmLogos.
|
|
586
|
+
- **Comfortable at:** 1–6 categories · 5–300 points
|
|
587
|
+
- **Record:** `{ value: number, category: string?, name: string? }`
|
|
588
|
+
|
|
589
|
+
### Scatter.createComet
|
|
590
|
+
|
|
591
|
+
Before/after comets: hollow tail dot, tapered gradient trail, solid head dot per record.
|
|
592
|
+
|
|
593
|
+
- **Use when:** Before→after movement in 2D — direction and magnitude per entity in a single tapered mark.
|
|
594
|
+
- **Not for:** More than two time points per entity (createTrajectory) or crowded fields — past ~40 comets the tails cross-hatch.
|
|
595
|
+
- **Comfortable at:** 3–40 points
|
|
596
|
+
- **Record:** `{ name: string?, x1: number, y1: number, x2: number, y2: number, category: string? }`
|
|
597
|
+
|
|
598
|
+
### Scatter.createContour
|
|
599
|
+
|
|
600
|
+
Filled density contours (d3.contourDensity) with optional contour outlines.
|
|
601
|
+
|
|
602
|
+
- **Use when:** The smooth shape of a dense 2D distribution — modes and ridges rather than counts; no per-point marks.
|
|
603
|
+
- **Not for:** Small n — below ~100 points contours hallucinate structure; and any use needing per-point identity or tooltips (the surface has no point marks).
|
|
604
|
+
- **Comfortable at:** 100+ points
|
|
605
|
+
- **Record:** `{ x: number (or d[0]), y: number (or d[1]) }`
|
|
606
|
+
|
|
607
|
+
### Scatter.createDiagonal
|
|
608
|
+
|
|
609
|
+
Scatter with parallel diagonal offset bands (y − x = constant) for two-metric comparisons (offense vs defense).
|
|
610
|
+
|
|
611
|
+
- **Use when:** Two metrics on the same scale where above/below the y = x line is the verdict — offense vs defense, expected vs actual.
|
|
612
|
+
- **Not for:** Metrics on different scales — the diagonal and its offset bands are meaningless; use createQuadrant or createBasic.
|
|
613
|
+
- **Comfortable at:** 5–100 points
|
|
614
|
+
- **Record:** `{ x: number, y: number, name: string? }`
|
|
615
|
+
|
|
616
|
+
### Scatter.createHeatmap
|
|
617
|
+
|
|
618
|
+
Grid heatmap with two modes: density (bins raw x/y points into a pixel grid, linear scales) and categorical (each record is a cell {x, y, value}, band scales).
|
|
619
|
+
|
|
620
|
+
- **Use when:** Density mode: where a large x/y cloud concentrates, binned to a pixel grid. Categorical mode: an x×y grid of cell values (day × hour matrices).
|
|
621
|
+
- **Not for:** Small n where individuals should stay visible (Scatter.createBasic); smooth density shape (createContour reads better than a coarse grid).
|
|
622
|
+
- **Comfortable at:** 300+ points — Density mode; categorical mode instead wants one record per grid cell (rows × columns ≲ ~400 cells).
|
|
623
|
+
- **Record:** `{ x: number, y: number, value: number? (categorical mode: cell value; unused in density mode) }`
|
|
624
|
+
|
|
625
|
+
### Scatter.createHexbin
|
|
626
|
+
|
|
627
|
+
Hexagonal density binning.
|
|
628
|
+
|
|
629
|
+
- **Use when:** Dense two-numeric clouds where overplotting hides structure — hexagonal density with counts per cell.
|
|
630
|
+
- **Not for:** Small n (below ~200 points a plain scatter keeps individuals visible and honest).
|
|
631
|
+
- **Comfortable at:** 200+ points
|
|
632
|
+
- **Record:** `{ x: number (or d[0]), y: number (or d[1]) }`
|
|
633
|
+
|
|
634
|
+
### Scatter.createKDE
|
|
635
|
+
|
|
636
|
+
2D kernel-density fill (d3.contourDensity, filled levels) with an optional overlay of raw data dots.
|
|
637
|
+
|
|
638
|
+
- **Use when:** Filled 2D density with an optional overlay of the raw dots — distribution shape plus individuals in one plot.
|
|
639
|
+
- **Not for:** Small n (density smoothing misleads) — and note tooltips exist only on the overlay dots; with showDots: false there is no hover surface.
|
|
640
|
+
- **Comfortable at:** 100–5000 points
|
|
641
|
+
- **Record:** `{ x: number, y: number, name: string? }`
|
|
642
|
+
|
|
643
|
+
### Scatter.createLogos
|
|
644
|
+
|
|
645
|
+
X/Y scatter that renders an image (logo) at each point instead of a dot.
|
|
646
|
+
|
|
647
|
+
- **Use when:** A small scatter where each point’s identity is the story — team or brand images as the marks.
|
|
648
|
+
- **Not for:** More than ~40 points (images occlude and stop being recognizable) or anonymous records — use Scatter.createBasic.
|
|
649
|
+
- **Comfortable at:** 5–40 points
|
|
650
|
+
- **Record:** `{ x: number, y: number, image | imageUrl | logo: string, name: string? }`
|
|
651
|
+
|
|
652
|
+
### Scatter.createMarginal
|
|
653
|
+
|
|
654
|
+
Scatter with per-category marginal histograms on the top and right panels.
|
|
655
|
+
|
|
656
|
+
- **Use when:** A relationship plus each variable’s distribution — per-category histograms on the top and right panels.
|
|
657
|
+
- **Not for:** Mini/tight layouts (the panels need room — aspect 1.25) or many categories (>4 histogram colors blend; use createMarginalSimple).
|
|
658
|
+
- **Comfortable at:** 30–2000 points · 1–4 categories
|
|
659
|
+
- **Record:** `{ x: number, y: number, category: string? }`
|
|
660
|
+
|
|
661
|
+
### Scatter.createMarginalKDE
|
|
662
|
+
|
|
663
|
+
Scatter with per-category smoothed KDE curves (Epanechnikov kernel) in the top and right marginal panels instead of histograms.
|
|
664
|
+
|
|
665
|
+
- **Use when:** The marginal scatter with smoothed KDE curves instead of histograms — polished editorial framing of the same question.
|
|
666
|
+
- **Not for:** Small n per category — smooth curves over a handful of points imply distributions the data cannot support; use createMarginal or createMarginalSimple.
|
|
667
|
+
- **Comfortable at:** 50–2000 points · 1–4 categories
|
|
668
|
+
- **Record:** `{ x: number, y: number, category: string? }`
|
|
669
|
+
|
|
670
|
+
### Scatter.createMarginalSimple
|
|
671
|
+
|
|
672
|
+
Single-color scatter with rug-mark marginals (tick lines on the bottom and left edges) instead of histogram panels.
|
|
673
|
+
|
|
674
|
+
- **Use when:** A relationship with lightweight rug-mark marginals — distribution context without the panel cost.
|
|
675
|
+
- **Not for:** Large n — rug ticks smear solid past a few hundred points; use createMarginal.
|
|
676
|
+
- **Comfortable at:** 20–500 points
|
|
677
|
+
- **Record:** `{ x: number, y: number, name: string? }`
|
|
678
|
+
|
|
679
|
+
### Scatter.createQuadrant
|
|
680
|
+
|
|
681
|
+
Four-quadrant analysis scatter with switchable center (average/zero/median or explicit centerX/centerY), tinted quadrant backgrounds, corner labels, and optional bubble sizing.
|
|
682
|
+
|
|
683
|
+
- **Use when:** Classifying items into four strategic cells around a defensible center — share × growth, effort × impact; optional bubble sizing for a third measure.
|
|
684
|
+
- **Not for:** Data with no meaningful center (plain scatter — a forced center implies a verdict) or dense clouds where labels and tooltips crowd.
|
|
685
|
+
- **Comfortable at:** 5–100 points
|
|
686
|
+
- **Record:** `{ x: number, y: number, category: string?, name: string?, size: number? }`
|
|
687
|
+
|
|
688
|
+
### Scatter.createTrajectory
|
|
689
|
+
|
|
690
|
+
Connected Catmull-Rom path trail per entity with start (hollow) and end (solid, hoverable) dots.
|
|
691
|
+
|
|
692
|
+
- **Use when:** Entities moving through a 2D metric space over time — each smoothed path is one entity’s history, start hollow, end solid.
|
|
693
|
+
- **Not for:** Many entities (paths tangle past ~10) or single-snapshot data (Scatter.createBasic); for just two time points use createComet.
|
|
694
|
+
- **Comfortable at:** 1–10 series · 2–20 points/series
|
|
695
|
+
- **Record:** Array of { name, category?, points: [{ x: number, y: number }, ...] } — point coordinates are fixed keys (no x/y accessors).
|
|
696
|
+
|
|
697
|
+
## Dot
|
|
698
|
+
|
|
699
|
+
### Dot.createBasic
|
|
700
|
+
|
|
701
|
+
Multi-category dot row chart — one dot per category per row on a shared value axis.
|
|
702
|
+
|
|
703
|
+
- **Use when:** Several measures per row compared as dots on one shared axis — compact multi-metric comparison without bar ink.
|
|
704
|
+
- **Not for:** One value per row (Bar.createBasic ranks faster) or measures on different scales — a shared axis forces one unit.
|
|
705
|
+
- **Comfortable at:** 2–20 rows · 2–5 series
|
|
706
|
+
- **Record:** `{ name: string, <categories[i].key>: number — one field per entry in categories }`
|
|
707
|
+
|
|
708
|
+
### Dot.createComet
|
|
709
|
+
|
|
710
|
+
Before→after comet rows — gradient tail into a head dot, trend-colored, optionally animated.
|
|
711
|
+
|
|
712
|
+
- **Use when:** Before→after per row rendered as motion — a gradient tail into a trend-colored head; more energetic than a dumbbell.
|
|
713
|
+
- **Not for:** Neutral/analytical contexts where the motion metaphor overstates — use createDumbbell.
|
|
714
|
+
- **Comfortable at:** 2–20 rows
|
|
715
|
+
- **Record:** `{ name: string, before: number, after: number }`
|
|
716
|
+
|
|
717
|
+
### Dot.createConfidence
|
|
718
|
+
|
|
719
|
+
Point estimate + confidence-interval band per row, with automatic significance coloring vs a control row.
|
|
720
|
+
|
|
721
|
+
- **Use when:** Point estimates with confidence intervals per row, auto-colored for significance vs a control row — experiment readouts.
|
|
722
|
+
- **Not for:** Data without real intervals — drawing invented CIs is worse than none; use Dot.createBasic.
|
|
723
|
+
- **Comfortable at:** 2–15 rows
|
|
724
|
+
- **Record:** `{ name: string, value: number, ci_low: number, ci_high: number, isControl: boolean? }`
|
|
725
|
+
|
|
726
|
+
### Dot.createDumbbell
|
|
727
|
+
|
|
728
|
+
Dumbbell row chart — two dots per row joined by a connector, with optional value labels.
|
|
729
|
+
|
|
730
|
+
- **Use when:** Two values per row (before/after, A vs B) with the gap as the message — scales to more rows than a slope chart because rows do not cross.
|
|
731
|
+
- **Not for:** More than two values per row (Dot.createBasic) or when crossing/rate-of-change matters more than gap size (Slope.createBasic).
|
|
732
|
+
- **Comfortable at:** 2–25 rows
|
|
733
|
+
- **Record:** `{ name: string, start: number, end: number }`
|
|
734
|
+
|
|
735
|
+
### Dot.createGradient
|
|
736
|
+
|
|
737
|
+
Dumbbell row chart with a gradient connector fading startColor→endColor to show direction.
|
|
738
|
+
|
|
739
|
+
- **Use when:** A dumbbell whose connector fades start→end color — adds direction when rows move both ways and the reader must see which way.
|
|
740
|
+
- **Not for:** When plain gap size suffices (createDumbbell) or when color already encodes a category — two color meanings collide.
|
|
741
|
+
- **Comfortable at:** 2–25 rows
|
|
742
|
+
- **Record:** `{ name: string, start: number, end: number }`
|
|
743
|
+
|
|
744
|
+
### Dot.createPercentile
|
|
745
|
+
|
|
746
|
+
Value dot over a temperature-colored percentile-band strip per row.
|
|
747
|
+
|
|
748
|
+
- **Use when:** A value in the context of its historical percentile bands per row — "how unusual is this?" (temperature vs climatology).
|
|
749
|
+
- **Not for:** Rows without a percentile history to stand on; for plain low/high extents use Bar.createRange.
|
|
750
|
+
- **Comfortable at:** 1–15 rows
|
|
751
|
+
- **Record:** `{ name: string (or season), value: number, p0: number, p10..p100: number — one field per entry in percentileKeys }`
|
|
752
|
+
|
|
753
|
+
### Dot.createUnitHistogram
|
|
754
|
+
|
|
755
|
+
Unit histogram — one circle per unit stacked vertically per category bin; both dimensions derived from data.
|
|
756
|
+
|
|
757
|
+
- **Use when:** Small-count distributions where each unit is real and countable — one circle per unit, stacked per bin.
|
|
758
|
+
- **Not for:** Counts beyond a few hundred (units blur into texture — Histogram.createBasic). Also the one fixed-size builder: it never fills its container.
|
|
759
|
+
- **Comfortable at:** 10–300 units · 2–15 bins
|
|
760
|
+
- **Record:** `{ category: string, count: number }`
|
|
761
|
+
|
|
762
|
+
## Histogram
|
|
763
|
+
|
|
764
|
+
### Histogram.createBasic
|
|
765
|
+
|
|
766
|
+
Single-series distribution histogram over a numeric field, with count/percent/density/cumulative y modes.
|
|
767
|
+
|
|
768
|
+
- **Use when:** The shape of one numeric distribution — skew, modes, spread — with count/percent/density/cumulative y modes.
|
|
769
|
+
- **Not for:** Small n (below ~30, show the individuals: beeswarm or Dot.createUnitHistogram) or categorical data (Bar.createBasic).
|
|
770
|
+
- **Comfortable at:** 30+ points · 5–60 bins
|
|
771
|
+
- **Record:** `{ value: number (field picked by valueAccessor) }`
|
|
772
|
+
|
|
773
|
+
### Histogram.createOverlay
|
|
774
|
+
|
|
775
|
+
Multi-series histogram over shared bin edges — translucent overlay by default, side-by-side grouped columns via style.
|
|
776
|
+
|
|
777
|
+
- **Use when:** Comparing a few distributions on shared bin edges — translucent overlay by default, grouped columns via style.
|
|
778
|
+
- **Not for:** More than ~4 series (translucent overlays turn to mud — use facet() or BoxPlot.createBasic).
|
|
779
|
+
- **Comfortable at:** 2–4 series · 60+ points
|
|
780
|
+
- **Record:** `{ value: number (field picked by valueAccessor) }`
|
|
781
|
+
|
|
782
|
+
## Donut
|
|
783
|
+
|
|
784
|
+
### Donut.createBasic
|
|
785
|
+
|
|
786
|
+
Share-of-whole arc chart — donut by default (innerRatio 0 = pie), min-slice "Other" aggregation, collision-managed callout labels.
|
|
787
|
+
|
|
788
|
+
- **Use when:** Part-of-whole at a single moment with a handful of slices; minSlice sweeps the tail into an auto-grayed "Other".
|
|
789
|
+
- **Not for:** More than ~6 slices or close values — angles compare worse than lengths (Bar.createBasic); composition over time (Area.createStacked).
|
|
790
|
+
- **Comfortable at:** 2–6 slices
|
|
791
|
+
- **Record:** `{ label: string, value: number >= 0 }`
|
|
792
|
+
|
|
793
|
+
## BoxPlot
|
|
794
|
+
|
|
795
|
+
### BoxPlot.createBasic
|
|
796
|
+
|
|
797
|
+
Five-number distribution boxes per category — raw rows or precomputed stats, Tukey/min-max whiskers, outliers, both orientations, deterministic jitter overlay.
|
|
798
|
+
|
|
799
|
+
- **Use when:** Summarizing distributions across categories when n is too large to show individuals — five-number boxes, whiskers, outliers.
|
|
800
|
+
- **Not for:** Small n per group (below ~5 the summary misleads — beeswarm shows the truth) or audiences unfamiliar with quartiles (histogram reads plainly).
|
|
801
|
+
- **Comfortable at:** 1–15 categories · 5+ points — points = per-group n in raw mode; precomputed-stats mode has no minimum.
|
|
802
|
+
- **Record:** `{ category: string, value: number }`
|
|
803
|
+
|
|
804
|
+
## Calendar
|
|
805
|
+
|
|
806
|
+
### Calendar.createHeatmap
|
|
807
|
+
|
|
808
|
+
Calendar heatmap — GitHub-style weekly grid, a month-per-row layout, or a vertical wall-calendar transpose; subtle month gutters; missing days render as quiet track cells.
|
|
809
|
+
|
|
810
|
+
- **Use when:** A daily metric’s rhythm over months or years — weekday patterns, streaks, seasonal texture; missing days render as quiet track cells.
|
|
811
|
+
- **Not for:** Non-daily data (aggregate first or use a line) and precise value comparison — color encodes value far more coarsely than position.
|
|
812
|
+
- **Comfortable at:** 30–1100 days
|
|
813
|
+
- **Record:** `{ date: 'YYYY-MM-DD' string or Date, value: number }`
|
|
814
|
+
|
|
815
|
+
## Sankey
|
|
816
|
+
|
|
817
|
+
### Sankey.createBasic
|
|
818
|
+
|
|
819
|
+
Flow diagram over link rows; nodes derived in first-appearance order, gradient ribbons by default, transitive hover highlighting.
|
|
820
|
+
|
|
821
|
+
- **Use when:** Quantities flowing from sources to targets — budgets, funnels, energy balances; ribbon width is the amount and flows conserve.
|
|
822
|
+
- **Not for:** Non-flow relationships or cyclic graphs (source→target must be acyclic); past ~30 nodes the diagram stops being readable.
|
|
823
|
+
- **Comfortable at:** 4–30 nodes · 3–60 links
|
|
824
|
+
- **Record:** `{ source: string, target: string, value: number > 0 }`
|
|
825
|
+
|
|
826
|
+
## Table
|
|
827
|
+
|
|
828
|
+
### Table.createBasic
|
|
829
|
+
|
|
830
|
+
Non-sortable DOM table (selector is an element ID without "#", targeting a <table>, or any container element — a <table><thead><tbody> is created inside non-table targets, and an unmatched id console.warns instead of failing silently).
|
|
831
|
+
|
|
832
|
+
- **Use when:** Exact values the reader will look up — when precision beats pattern; short, fixed-order lists.
|
|
833
|
+
- **Not for:** Perceiving trends or comparisons (that is what charts are for) or lists the reader will want to rank — use Table.createSortable.
|
|
834
|
+
- **Comfortable at:** 1–50 rows · 2–10 columns
|
|
835
|
+
- **Record:** Flat row object keyed by columns[].key — values string | number; bar/heatmap column values are parseFloat()ed (non-numeric → 0).
|
|
836
|
+
|
|
837
|
+
### Table.createCombined
|
|
838
|
+
|
|
839
|
+
Sortable DOM table combining bar cells and heatmap cells (pass both barColumns and heatmapColumns; no missing-config warning, d3-chart-factory.js L11633-11636).
|
|
840
|
+
|
|
841
|
+
- **Use when:** The dashboard-grade table: bar columns for magnitudes plus heatmap columns for rates, in one sortable surface.
|
|
842
|
+
- **Not for:** Simple lists — reach for the lighter table variants first; every treated column adds scan cost.
|
|
843
|
+
- **Comfortable at:** 2–100 rows · 3–12 columns
|
|
844
|
+
- **Record:** Flat row object keyed by columns[].key — values string | number; bar/heatmap column values are parseFloat()ed (non-numeric → 0). Bar domain [0, max]; heatmap domain [min, max].
|
|
845
|
+
|
|
846
|
+
### Table.createSortable
|
|
847
|
+
|
|
848
|
+
Sortable DOM table with click-to-sort headers and sort indicators (selector is an element ID without "#", targeting a <table>, or any container element — a <table><thead><tbody> is created inside non-table targets, and an unmatched id console.warns instead of failing silently).
|
|
849
|
+
|
|
850
|
+
- **Use when:** Lookup plus reader-driven ranking — click-to-sort columns for lists where different readers care about different columns.
|
|
851
|
+
- **Not for:** Data with one obvious story — a chart tells it faster; sorting is for exploration, not narration.
|
|
852
|
+
- **Comfortable at:** 2–100 rows · 2–10 columns
|
|
853
|
+
- **Record:** Flat row object keyed by columns[].key — values string | number; bar/heatmap column values are parseFloat()ed (non-numeric → 0).
|
|
854
|
+
|
|
855
|
+
### Table.createWithBars
|
|
856
|
+
|
|
857
|
+
Sortable DOM table with animated percent-bar cells in designated columns (console.warn if barColumns is missing, d3-chart-factory.js L11606-11608).
|
|
858
|
+
|
|
859
|
+
- **Use when:** A sortable table where one or two numeric columns deserve at-a-glance magnitude — in-cell percent bars beside the numbers.
|
|
860
|
+
- **Not for:** Many bar columns (past two, the table reads as a failed bar chart — use Bar.createBasic per metric or Dot.createBasic).
|
|
861
|
+
- **Comfortable at:** 2–100 rows · 2–10 columns
|
|
862
|
+
- **Record:** Flat row object keyed by columns[].key — values string | number; bar column values are parseFloat()ed (non-numeric → 0) and scaled against [0, max].
|
|
863
|
+
|
|
864
|
+
### Table.createWithHeatmap
|
|
865
|
+
|
|
866
|
+
Sortable DOM table with heatmap-box cells (auto-contrast text) in designated columns (console.warn if heatmapColumns is missing, d3-chart-factory.js L11621-11623).
|
|
867
|
+
|
|
868
|
+
- **Use when:** A sortable table where value intensity should pop — heatmap cells (sequential or diverging) behind the numbers.
|
|
869
|
+
- **Not for:** Precise cross-column comparison of the tinted values — color supports scanning, not reading; keep the numbers visible.
|
|
870
|
+
- **Comfortable at:** 2–100 rows · 2–12 columns
|
|
871
|
+
- **Record:** Flat row object keyed by columns[].key — values string | number; heatmap column values are parseFloat()ed (non-numeric → 0) and scaled against [min, max].
|
|
872
|
+
|