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.
@@ -0,0 +1,134 @@
1
+ # Color guide — choosing, not configuring
2
+
3
+ How to make color *decisions* in Chart Factory. For the option reference see
4
+ [API.md](API.md); for the palette surface see
5
+ [Palettes (`ChartFactory.palettes`)](API.md#palettes-chartfactorypalettes).
6
+ This guide is deliberately bounded to Chart Factory's builders — it is not a
7
+ general visualization textbook.
8
+
9
+ ## Rule zero: omit color options
10
+
11
+ **The defaults are the house style.** Single-series charts use `--chart-primary`
12
+ (brand blue); multi-series charts use the Set2 categorical palette
13
+ (`--area-color-1..8`, CVD-safe, matched across families, theme-aware). Most
14
+ configs should pass **no color options at all** — every color you hardcode is
15
+ one that stops re-theming, stops dark-mode adaptation, and drifts from the
16
+ house look. Reach for a color option only when one of the decisions below
17
+ genuinely applies.
18
+
19
+ ## Pick the palette *type* from the data, not the look
20
+
21
+ | Your data is… | Type | Test | Examples |
22
+ |---|---|---|---|
23
+ | Ordered, one direction (counts, density, intensity) | **Sequential** | "Is 80 *more of the same thing* than 20?" | `Viridis`, `Blues`, `YlOrRd` |
24
+ | Ordered around a **meaningful midpoint** (zero, average, target, 50%) | **Diverging** | "Is the midpoint a real boundary — above vs below?" | `RdBu`, `Temperature`, `PuOr` |
25
+ | Named categories with no order | **Categorical** | "Would reordering the legend lose nothing?" | `Set2` (default), `OkabeIto`, `Dark2` |
26
+ | Mostly noise, a few values that must pop | **Alert** | "Do only the extremes matter?" | `OrangeAlert`, `ForestAlert` |
27
+
28
+ The classic mistake is diverging-for-sequential: a red-to-blue ramp on plain
29
+ counts invents a midpoint the data doesn't have. The reverse mistake hides
30
+ one: profit/loss on a `Blues` ramp buries the zero crossing. **Ask where the
31
+ midpoint is. No answer → sequential. Real answer → diverging, and pin it with
32
+ `colorDomain: [-max, max]` so the neutral center sits on the true zero.**
33
+
34
+ - Heatmap/hexbin/contour/calendar take `colorScheme: 'Viridis'` (or any
35
+ sequential/diverging palette or d3 interpolator name).
36
+ - `Bar.createDiverging` colors by sign (green/red trend semantics) and
37
+ `Bar.createLikert` builds its own diverging ramp from the temperature
38
+ tokens — both already encode direction; don't override either with a
39
+ categorical palette.
40
+ - Never use a sequential ramp for categories — adjacent steps are
41
+ indistinguishable as fills; that's what categorical palettes are for.
42
+
43
+ ## Categories: fewer, then gray the rest
44
+
45
+ Legibility collapses past **6–8 categories** — that's why Set2 has 8 colors,
46
+ not 20. Past that, don't grab a 12-color palette; **shrink the category count**:
47
+
48
+ ```js
49
+ const top = ChartFactory.data.topN(rows, 'revenue', 5, { by: 'segment', other: 'Other' });
50
+ ```
51
+
52
+ The residual bucket is handled for you: **a series or slice named "Other" is
53
+ automatically colored neutral gray** (`--chart-neutral`) by the default
54
+ palettes, and it does *not* consume a palette slot — real categories keep
55
+ their colors. This applies to stacked/normalized/streamgraph areas,
56
+ stacked/grouped/vertical bars, multi-line, histogram overlays, donut
57
+ (including its own `minSlice` fold), and the derived shell legend
58
+ (`legend: true`). Passing explicit `colors` or `colorMap` disables it —
59
+ that's the escape hatch when "Other" is a genuine category.
60
+
61
+ Keep "Other" **last** in `keys`/data order (`data.topN` already does this):
62
+ gray reads as "the remainder", and remainders belong at the end.
63
+
64
+ ## Reserved meanings — don't spend them on categories
65
+
66
+ Some colors already mean something in this system. Using them as arbitrary
67
+ category colors creates false signals:
68
+
69
+ - **Green/red = direction.** `--chart-trend-up` / `--chart-trend-down` drive
70
+ slope-trend coloring, diverging bars, and up/down markers. A category that
71
+ happens to be green will be read as "good".
72
+ - **Red = anomaly** (`--chart-anomaly`): pulse/forecast anomaly markers.
73
+ - **Gray = de-emphasis**: the residual bucket, muted series in
74
+ highlight modes, context layers. Never use gray for a category you want
75
+ the reader to care about.
76
+ - Avoid red+green as a categorical *pair* entirely — it's the most common
77
+ color-vision confusion. If direction matters and accessibility is critical,
78
+ pair color with another channel the way the builders already do
79
+ (slope direction, diverging sides, anomaly markers are all positional too).
80
+
81
+ For context/reference series (a benchmark line behind the real data, last
82
+ year's shape), use `extraLayers` with a neutral:
83
+ `{ type: 'line', y: 'lastYear', color: 'var(--chart-neutral)', behind: true }`.
84
+
85
+ ## Accessibility (CVD)
86
+
87
+ The defaults are already safe: **Set2** (categorical) and **Viridis**-family
88
+ (sequential) are colorblind-safe. If you deviate, check before shipping:
89
+
90
+ ```js
91
+ ChartFactory.palettes.isColorblindSafe('RdYlGn') // false — avoid
92
+ ChartFactory.palettes.list('categorical')
93
+ .filter(ChartFactory.palettes.isColorblindSafe) // safe alternatives
94
+ ```
95
+
96
+ `OkabeIto` is the strictest categorical choice (designed for all CVD types) —
97
+ but note its 8th color is pure black, which disappears on dark backgrounds;
98
+ slice it to 7 if you need dark-mode parity. `RdYlGn` (“traffic light”) is the
99
+ most-requested unsafe palette; `RdYlBu` or `Temperature` carry the same
100
+ bad-to-good reading safely.
101
+
102
+ ## Dark mode: already handled
103
+
104
+ Sequential/diverging ramps re-anchor their pale end toward the surface under
105
+ `[data-theme="dark"]`; the categorical tokens have dark variants; charts
106
+ re-theme on `ChartFactory.rerenderAll()` (or `setTheme(name)`, which does
107
+ both steps). **Do not compensate manually** — no "dark mode palettes", no
108
+ hand-lightened hexes. If you pass literal hexes, you own their dark-mode
109
+ behavior; prefer `var(--…)` token strings (they re-theme via CSS) or palette
110
+ *names* (re-resolved per render).
111
+
112
+ Dark mode is one instance of the general theming contract — external
113
+ "companion themes" (own token scope, texture fills, mark filters) are
114
+ documented in [theming.md](theming.md).
115
+
116
+ ## Scenario → palette
117
+
118
+ | Scenario | Use |
119
+ |---|---|
120
+ | Any single-series chart | nothing — `--chart-primary` |
121
+ | Any multi-series chart, ≤8 categories | nothing — Set2 default |
122
+ | >8 categories | `data.topN(...)` + auto-grayed "Other" |
123
+ | Heatmap / density / calendar intensity | `colorScheme: 'Viridis'` (or `Blues` for corporate-quiet) |
124
+ | Above/below zero, plus/minus, diff vs target | `colorScheme: 'RdBu'` or `'Temperature'`, domain pinned symmetric |
125
+ | Performance bad→good with CVD safety | `RdYlBu` or `Temperature` (not `RdYlGn`) |
126
+ | Strict accessibility requirement | `colors: 'OkabeIto'` (7 colors on dark) |
127
+ | Benchmark/context series | `extraLayers` + `var(--chart-neutral)`, `behind: true` |
128
+ | Status/semantic UI values | `colors: 'Status'` (success/warning/error/info) |
129
+ | Yes/no, on/off | `colors: 'Binary'` |
130
+
131
+ One consistency rule across a dashboard: **same entity, same color,
132
+ everywhere.** When several charts show the same categories, pass the same
133
+ explicit `colors`/`colorMap` (or use `linkGroup`'s shared `colorFor`) rather
134
+ than letting encounter order assign different hues per chart.