plotagents 0.0.0-stage → 0.5.0
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/AGENTS.md +78 -0
- package/LICENSE +21 -0
- package/README.md +205 -2
- package/bin/plotagents.js +49 -0
- package/dist/plotagents-geo.js +79 -0
- package/dist/plotagents.js +7954 -0
- package/docs/api.md +154 -0
- package/docs/charts.md +218 -0
- package/docs/getting-started.md +138 -0
- package/docs/rendering.md +47 -0
- package/docs/themes.md +86 -0
- package/index.d.ts +213 -0
- package/package.json +74 -4
- package/schema.json +895 -0
- package/src/axes.js +144 -0
- package/src/charts/bar.js +339 -0
- package/src/charts/categorical.js +471 -0
- package/src/charts/chord.js +218 -0
- package/src/charts/choropleth.js +559 -0
- package/src/charts/dist.js +513 -0
- package/src/charts/donut.js +232 -0
- package/src/charts/flowchart.js +407 -0
- package/src/charts/heatmap.js +76 -0
- package/src/charts/kpi.js +97 -0
- package/src/charts/line.js +419 -0
- package/src/charts/network.js +344 -0
- package/src/charts/pyramid.js +177 -0
- package/src/charts/radar.js +152 -0
- package/src/charts/regplot.js +364 -0
- package/src/charts/sankey.js +133 -0
- package/src/charts/scatter.js +136 -0
- package/src/charts/sunburst.js +135 -0
- package/src/charts/treemap.js +194 -0
- package/src/context.js +10 -0
- package/src/dashed.js +90 -0
- package/src/data.js +137 -0
- package/src/facet.js +84 -0
- package/src/format.js +171 -0
- package/src/frame.js +157 -0
- package/src/geo/chile.js +7 -0
- package/src/geo/data/chile-regiones.js +12 -0
- package/src/geo/data/usa-estados.js +12 -0
- package/src/geo/data/world.js +12 -0
- package/src/geo/projection.js +152 -0
- package/src/geo/registry.js +31 -0
- package/src/geo/usa.js +8 -0
- package/src/geo/world.js +7 -0
- package/src/hierarchy.js +139 -0
- package/src/index.js +271 -0
- package/src/infer.js +49 -0
- package/src/interact.js +362 -0
- package/src/lint.js +137 -0
- package/src/palette.js +59 -0
- package/src/runtime.js +304 -0
- package/src/scale.js +171 -0
- package/src/schema.js +181 -0
- package/src/stats.js +246 -0
- package/src/svg.js +85 -0
- package/src/text.js +61 -0
- package/src/theme.js +224 -0
package/docs/api.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# API reference
|
|
2
|
+
|
|
3
|
+
```js
|
|
4
|
+
import {
|
|
5
|
+
plot, safePlot, lint, describe, render, toHTML, configure,
|
|
6
|
+
schema, toolDefinition, validatePalette, registerGeo,
|
|
7
|
+
} from 'plotagents';
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Full types are in [`index.d.ts`](../index.d.ts) and every spec option in [`schema.json`](../schema.json).
|
|
11
|
+
|
|
12
|
+
## Drawing
|
|
13
|
+
|
|
14
|
+
### `plot(spec)`
|
|
15
|
+
|
|
16
|
+
Draws a chart and returns an SVG string. Works in Node, Deno, Bun and the browser. Throws a `PlotError` when the spec cannot be drawn; the error's `hint` says how to fix it.
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
const svg = plot({ type: 'bar', data, x: 'region', y: 'sales', title: 'Biobío leads with 30%' });
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### `safePlot(spec)`
|
|
23
|
+
|
|
24
|
+
The version to expose as an agent tool: it never throws.
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
const { ok, svg, type, description, errors, warnings, suggestions } = safePlot(spec);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
| Field | Meaning |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `ok` | `true` when the chart was drawn. |
|
|
33
|
+
| `svg` | The SVG string, or `null`. |
|
|
34
|
+
| `type` | The resolved chart type (useful when `type` was inferred). |
|
|
35
|
+
| `description` | Alt text for the chart. |
|
|
36
|
+
| `errors` | Problems that stop the chart: `{ code, message, fix }`. |
|
|
37
|
+
| `warnings` | Problems that make the chart misleading or hard to read. |
|
|
38
|
+
| `suggestions` | Design improvements, such as a better title or `highlight`. |
|
|
39
|
+
|
|
40
|
+
### `lint(spec)`
|
|
41
|
+
|
|
42
|
+
Reviews a spec without drawing it and returns `{ ok, type, errors, warnings, suggestions }`. Messages are written in Spanish; each one carries a `fix` with the change to make.
|
|
43
|
+
|
|
44
|
+
### `describe(spec)`
|
|
45
|
+
|
|
46
|
+
Returns a text description of the chart, such as "Highest: Biobío with 1,240 (30%)…". The same text goes into the SVG's `<desc>` for screen readers.
|
|
47
|
+
|
|
48
|
+
## In the browser
|
|
49
|
+
|
|
50
|
+
### `render(target, spec, opts?)`
|
|
51
|
+
|
|
52
|
+
Draws into an element (a CSS selector or a DOM node) with tooltips, responsive width, time sliders, a range slider for time series, drill-down for treemaps and sunbursts, and click-to-focus for networks.
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
const chart = plotagents.render('#chart', spec);
|
|
56
|
+
chart.update({ ...spec, highlight: 'Ñuble' }); // redraw
|
|
57
|
+
chart.destroy(); // remove listeners and markup
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
| Option | Meaning |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `controls` | `false` hides sliders and play buttons. |
|
|
63
|
+
|
|
64
|
+
The handle exposes `svg` (the current `<svg>` element) and `spec`.
|
|
65
|
+
|
|
66
|
+
### `attach(root)`
|
|
67
|
+
|
|
68
|
+
Adds tooltips to a container that already holds a plotagents SVG, for example one rendered on the server.
|
|
69
|
+
|
|
70
|
+
### `toHTML(specs, opts?)`
|
|
71
|
+
|
|
72
|
+
A self-contained HTML page with one or more interactive charts and the library inlined.
|
|
73
|
+
|
|
74
|
+
| Option | Default | Meaning |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `title` | `"Gráficos"` | Page title. |
|
|
77
|
+
| `lang` | `"es"` | Page language. |
|
|
78
|
+
| `columns` | `1` | Two or more lay charts out in a grid. |
|
|
79
|
+
|
|
80
|
+
## Configuration
|
|
81
|
+
|
|
82
|
+
### `configure(options)`
|
|
83
|
+
|
|
84
|
+
Sets defaults for every later chart; the spec always wins.
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
configure({ font: 'Poppins', textScale: 1.5, locale: 'en' });
|
|
88
|
+
configure(); // returns the current defaults
|
|
89
|
+
configure(null); // clears them
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
It accepts any spec option: `font`, `textScale`, `locale`, `theme`, `palette`, `ramp` and so on.
|
|
93
|
+
|
|
94
|
+
### `validatePalette(colors, opts?)`
|
|
95
|
+
|
|
96
|
+
Checks a custom palette with the same tests the built-in one passes. See [themes](themes.md#validating-a-palette).
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
validatePalette(['#223C6A', '#E69B0A', '#D21428'], { mode: 'light', surface: '#FFFFFF' });
|
|
100
|
+
// → { ok: false, checks: [{ name: 'lightness', status: 'fail', detail: '…' }, …] }
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### `registerGeo(name, featureCollection, aliases?)`
|
|
104
|
+
|
|
105
|
+
Registers your own GeoJSON so specs can use `geo: name`. Chile and the world are built in.
|
|
106
|
+
|
|
107
|
+
## For agents
|
|
108
|
+
|
|
109
|
+
### `toolDefinition({ name?, flavor? })`
|
|
110
|
+
|
|
111
|
+
Returns a tool definition built from the JSON Schema: `flavor: "anthropic"` (default) gives `{ name, description, input_schema }`, `flavor: "openai"` gives `{ type: "function", function: { … } }`.
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
115
|
+
import { safePlot, toolDefinition } from 'plotagents';
|
|
116
|
+
|
|
117
|
+
const client = new Anthropic();
|
|
118
|
+
const res = await client.messages.create({
|
|
119
|
+
model: process.env.ANTHROPIC_MODEL,
|
|
120
|
+
max_tokens: 4096,
|
|
121
|
+
tools: [toolDefinition()],
|
|
122
|
+
messages: [{ role: 'user', content: 'Plot the distribution of service times per clinic: …' }],
|
|
123
|
+
});
|
|
124
|
+
const call = res.content.find((b) => b.type === 'tool_use');
|
|
125
|
+
const { ok, svg, errors } = safePlot(call.input);
|
|
126
|
+
// If !ok, send `errors` back as the tool_result: every error carries a `fix`.
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### `schema`
|
|
130
|
+
|
|
131
|
+
The JSON Schema of a spec, also published as [`schema.json`](../schema.json) and importable as `plotagents/schema.json`.
|
|
132
|
+
|
|
133
|
+
## Helpers
|
|
134
|
+
|
|
135
|
+
| Export | Meaning |
|
|
136
|
+
|---|---|
|
|
137
|
+
| `TYPES` | Every chart type name. |
|
|
138
|
+
| `themes`, `getTheme(t)`, `themeFor(spec)` | Built-in themes and the resolved theme of a spec. |
|
|
139
|
+
| `palettes`, `ramps` | Named categorical palettes and sequential/diverging ramps. |
|
|
140
|
+
| `timeValues(spec)` | Sorted values of the `time` field. |
|
|
141
|
+
| `makeFormat(fmt, locale, values)` | The number formatter a chart would use. |
|
|
142
|
+
| `formatNumber(v, decimals, locale)`, `formatCompact(v, locale)` | Locale-aware number formatting. |
|
|
143
|
+
| `squarify(items, x, y, w, h)` | The treemap layout algorithm. |
|
|
144
|
+
| `bundleSource()` | Node only: the browser build as a string, for inlining. |
|
|
145
|
+
|
|
146
|
+
## Package entry points
|
|
147
|
+
|
|
148
|
+
| Import | Contents |
|
|
149
|
+
|---|---|
|
|
150
|
+
| `plotagents` | Everything above (ES module). |
|
|
151
|
+
| `plotagents/schema.json` | The JSON Schema. |
|
|
152
|
+
| `plotagents/browser` | `dist/plotagents.js`: a `<script>` that defines `window.plotagents`. |
|
|
153
|
+
| `plotagents/browser/geo` | `dist/plotagents-geo.js`: Chile and world geometries for the browser build. |
|
|
154
|
+
| `plotagents/geo/chile`, `plotagents/geo/world` | The geometries as ES modules. |
|
package/docs/charts.md
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Chart reference
|
|
2
|
+
|
|
3
|
+
Every chart takes a spec with `data` (rows) plus field names for each role. The options below are specific to each type; the [common options](#common-options) apply to all of them. Every option is also described in [`schema.json`](../schema.json).
|
|
4
|
+
|
|
5
|
+
## Common options
|
|
6
|
+
|
|
7
|
+
| Option | Meaning |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `type` | Chart type or alias. Omit (or `"auto"`) to infer it from the data. |
|
|
10
|
+
| `data` | Array of row objects. `{ A: 1, B: 2 }` and arrays of numbers are accepted too. |
|
|
11
|
+
| `title` | The finding, as a sentence. Wraps; never truncated. |
|
|
12
|
+
| `subtitle` | How to read the chart: measure, unit, period. |
|
|
13
|
+
| `source` | Data source, shown small at the bottom right. |
|
|
14
|
+
| `series` / `hue` | Field that splits the data into colored groups. |
|
|
15
|
+
| `highlight` | One name or a list: emphasize these, mute the rest. |
|
|
16
|
+
| `colors` | `{ "name": "#hex" }` to pin an entity's color. |
|
|
17
|
+
| `format`, `xFormat`, `yFormat` | `"number" \| "compact" \| "percent" \| "currency" \| "integer"` or `{ prefix, suffix, decimals, currency }`. |
|
|
18
|
+
| `locale` | `"es"` (default: `1.234,5`) or `"en"` (`1,234.5`). |
|
|
19
|
+
| `xLabel`, `yLabel` | Axis titles (horizontal, never rotated). |
|
|
20
|
+
| `xScale`, `yScale` | `"log"` where it makes sense. |
|
|
21
|
+
| `xDomain`, `yDomain` | Fixed `[min, max]`. |
|
|
22
|
+
| `refLines` | `[{ y: 600, label: "Target" }]` (or `x`, or `value` for bars). |
|
|
23
|
+
| `facet` (`col`), `columns`, `sharey`, `sharex` | Small multiples, one panel per value. |
|
|
24
|
+
| `time`, `timeValue`, `slider`, `play` | One period at a time with a slider in the browser; `slider: false` removes it, `play: false` hides only the play button. |
|
|
25
|
+
| `compare` | With `time`: compare against `"previous"` (default), `"first"`, a given period, or `false`. |
|
|
26
|
+
| `theme`, `palette`, `ramp` | See [themes](themes.md). |
|
|
27
|
+
| `font`, `textScale` | Typeface (Google Fonts name, CSS stack or `{ family, import }`) and a text-size multiplier (aliases `fontScale`, `labelScale`). Set them for every chart with `configure()`. |
|
|
28
|
+
| `width`, `height`, `padding` | Pixels. Height is automatic for horizontal bars, heatmaps, KPI tiles, and narrow treemaps, sunbursts and radars. |
|
|
29
|
+
| `legend` | `false` hides the legend. |
|
|
30
|
+
|
|
31
|
+
## Compare
|
|
32
|
+
|
|
33
|
+
### `bar` · `column` · `barplot` · `count` (`countplot`)
|
|
34
|
+
|
|
35
|
+
`x` category, `y` value. Columns for few short categories, horizontal bars when labels are long or there are many; values at the bar tips and no axis when every bar is labeled.
|
|
36
|
+
|
|
37
|
+
| Option | Meaning |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `stack` | `true` stacks series; `"percent"` stacks to 100%. |
|
|
40
|
+
| `horizontal` | Force orientation. |
|
|
41
|
+
| `sort` | `"desc"` (default for categories), `"asc"`, `"none"`, `"alpha"`. Years and dates keep their order. |
|
|
42
|
+
| `top` | Keep the N largest, fold the rest into "Other" (automatic past 40 categories). |
|
|
43
|
+
| `ci`, `errorbar`, `estimator` | `barplot`: mean ± 95% CI (`"ci"`), `"sd"` or `"se"` error bars. |
|
|
44
|
+
| `aggregate` | `"sum"` (default), `"mean"`, `"max"`, `"min"`, `"count"`, `"first"`, `"last"`. `countplot` counts rows. |
|
|
45
|
+
| `labels` | `true`, `false` or `"auto"`. |
|
|
46
|
+
| `barWidth` | Max bar thickness in px. |
|
|
47
|
+
|
|
48
|
+
## Time
|
|
49
|
+
|
|
50
|
+
### `line` · `area`
|
|
51
|
+
|
|
52
|
+
`x` date (`"2024-03"`, `"2024-03-15"`, `Date`), year or number; `y` value or list of fields (wide table → series).
|
|
53
|
+
|
|
54
|
+
| Option | Meaning |
|
|
55
|
+
|---|---|
|
|
56
|
+
| `labels` | End labels (default on for every series that fits; colliding labels get leader lines). |
|
|
57
|
+
| `annotations` | `[{ x: "2024-01", label: "New branch" }]`. |
|
|
58
|
+
| `index` | Rebase each series to 100 at its first value. |
|
|
59
|
+
| `ci`, `errorbar`, `estimator` | seaborn `lineplot`: mean and confidence band when there are several rows per x. |
|
|
60
|
+
| `curve` | `"linear"` (default), `"smooth"` (monotone, never invents peaks), `"step"`. |
|
|
61
|
+
| `zero` | Force the y axis to include zero (on by default for areas and when it costs little). |
|
|
62
|
+
| `points` | Markers on every point (automatic for short series). |
|
|
63
|
+
| `stack` | Areas: `true` (default with series), `"percent"`, or `false` to overlap. |
|
|
64
|
+
| `fill` | Single line: `false` removes the soft gradient. |
|
|
65
|
+
| `xRange` | Visible range `[from, to]`; this is what the browser slider sets. |
|
|
66
|
+
|
|
67
|
+
## Relationships
|
|
68
|
+
|
|
69
|
+
### `scatter` · `bubble`
|
|
70
|
+
|
|
71
|
+
`x`, `y` numeric; `size` for bubbles (area-proportional); `label` field with `labels` (number of points or list of names) for selective labels; `trend: true` adds a least-squares line with R².
|
|
72
|
+
|
|
73
|
+
### `regplot` (`lmplot`) · `residplot` · `jointplot`
|
|
74
|
+
|
|
75
|
+
| Option | Meaning |
|
|
76
|
+
|---|---|
|
|
77
|
+
| `ci` | Confidence band level (95 by default, `false` to hide). |
|
|
78
|
+
| `order` | Polynomial degree. |
|
|
79
|
+
| `lowess` (+ `frac`) | Local regression instead of a line. |
|
|
80
|
+
| `logx` | Fit `y ~ log(x)` and use a log x axis. |
|
|
81
|
+
| `scatter` | `false` hides the points. |
|
|
82
|
+
| `truncate` | Draw the fit only over the data range (default `true`). |
|
|
83
|
+
| `annotate` | Show R² and n. |
|
|
84
|
+
| `label`, `labels` | Name selected points. |
|
|
85
|
+
| `kind` | `jointplot`: `"scatter"` or `"reg"`. |
|
|
86
|
+
| `marginal` | `jointplot`: `"hist"` or `"kde"`. |
|
|
87
|
+
|
|
88
|
+
### `radar` (`spider`)
|
|
89
|
+
|
|
90
|
+
Long data (`axis`, `value`, `series`) or wide data (`axes: [fields]`, one row per series with `label`). `normalize` scales each axis to its own maximum (tooltips keep raw values); `grid: "polygon" | "circle"`; `domain`. Best with ≤ 4 series.
|
|
91
|
+
|
|
92
|
+
## Distributions
|
|
93
|
+
|
|
94
|
+
### `hist` (`histplot`) · `kde` (`kdeplot`) · `ecdf`
|
|
95
|
+
|
|
96
|
+
| Option | Meaning |
|
|
97
|
+
|---|---|
|
|
98
|
+
| `bins`, `binWidth` | Bin count or width (default: Freedman–Diaconis, rounded to nice edges). |
|
|
99
|
+
| `stat` | `"count"`, `"percent"`, `"density"`, `"probability"`. |
|
|
100
|
+
| `kde` | Overlay a density curve on the histogram. |
|
|
101
|
+
| `multiple` | With `hue`: `"layer"`, `"stack"`, `"dodge"`, `"fill"`. |
|
|
102
|
+
| `element` | `"bars"` or `"step"`. |
|
|
103
|
+
| `cumulative` | Cumulative distribution. |
|
|
104
|
+
| `fill`, `bwAdjust`, `cut`, `clip`, `rug` | Density options. |
|
|
105
|
+
| `complementary` | `ecdf`: plot 1 − F(x). |
|
|
106
|
+
|
|
107
|
+
### `box` · `violin` · `strip` · `swarm` (`beeswarm`) · `point` (`pointplot`)
|
|
108
|
+
|
|
109
|
+
`x` category and `y` measure (swap them, or set `horizontal`, for horizontal). Long labels or many categories switch to horizontal automatically.
|
|
110
|
+
|
|
111
|
+
| Option | Meaning |
|
|
112
|
+
|---|---|
|
|
113
|
+
| `points` | Box: overlay the observations. |
|
|
114
|
+
| `showMeans` | Mark the mean. |
|
|
115
|
+
| `inner` | Violin: `"box"` (default), `"quartiles"`, `"points"`, `"none"`. |
|
|
116
|
+
| `split` | Violin with two `hue` levels: one half each. |
|
|
117
|
+
| `scale` | Violin width: `"width"`, `"area"`, `"count"`. |
|
|
118
|
+
| `jitter`, `dodge`, `radius` | Strip and swarm layout. |
|
|
119
|
+
| `estimator`, `errorbar` | Point plot: `"mean"`/`"median"`/`"sum"` and `"ci"`/`"sd"`/`"se"`/`"pi"`. |
|
|
120
|
+
| `order`, `hueOrder` | Category and group order. |
|
|
121
|
+
|
|
122
|
+
## Part-to-whole
|
|
123
|
+
|
|
124
|
+
### `treemap` · `sunburst`
|
|
125
|
+
|
|
126
|
+
`path: [level1, level2, …]` of any depth (or `group` + `label`), `value`. Rows with the same path are summed.
|
|
127
|
+
|
|
128
|
+
| Option | Meaning |
|
|
129
|
+
|---|---|
|
|
130
|
+
| `depth` | Show only the first N levels. |
|
|
131
|
+
| `focus` | Path to drill into, e.g. `["Mining"]`. In the browser, clicking a group does it and breadcrumbs go back. |
|
|
132
|
+
| `showValues` | Append values to the labels. |
|
|
133
|
+
| `colorBy` | `"change"` colors tiles by % change vs the comparison period (growth cool, decline warm); `"label"` colors a single-level treemap by item. |
|
|
134
|
+
| `shade` | Subtle lightness steps inside each category. Off by default: one solid color per category. |
|
|
135
|
+
| `compareData` | Rows of an earlier period to compute changes without `time`. |
|
|
136
|
+
| `hole`, `centerValue`, `centerLabel` | Sunburst center. |
|
|
137
|
+
|
|
138
|
+
Areas are exact: there are no header bands taking space from a group. Categories are told apart by one solid color, a 4 px gap between level-1 groups (2 px and 1 px deeper) and the legend, which lists each group's share. With `time`, every tile also shows its change vs the previous period (▲ 12%). Colors belong to the level-1 group and are computed over the full tree and across all periods, so they never change when drilling or sliding.
|
|
139
|
+
|
|
140
|
+
### `pyramid` (`populationpyramid`)
|
|
141
|
+
|
|
142
|
+
Age groups (`age` or `y`), two sides (`sex`, `series` or `hue`) and a value. Back-to-back bars with the ages in the center column; `percent: true` for shares of the population; `compareData`, or `time` + `compare`, draws an earlier year as a stepped outline.
|
|
143
|
+
|
|
144
|
+
### `donut` · `pie`
|
|
145
|
+
|
|
146
|
+
`label`, `value`. More than `maxSlices` (6) folds into "Other".
|
|
147
|
+
|
|
148
|
+
| Option | Meaning |
|
|
149
|
+
|---|---|
|
|
150
|
+
| `labels` | `"auto"`, `"inside"`, `"outside"` (leader lines), `"legend"`, `false`. |
|
|
151
|
+
| `half` | Semicircle. |
|
|
152
|
+
| `thickness` | Ring thickness (pie is `1`). |
|
|
153
|
+
| `sort` | `"desc"` or `"none"`. |
|
|
154
|
+
| `centerValue`, `centerLabel` | Donut center. |
|
|
155
|
+
|
|
156
|
+
## Maps
|
|
157
|
+
|
|
158
|
+
### `choropleth` (`map`) · `symbolmap` (`bubblemap`)
|
|
159
|
+
|
|
160
|
+
Chilean regions (`geo: "chile"`, default), US states (`geo: "usa"`) and world countries (`geo: "world"`) ship with the library; any GeoJSON FeatureCollection works too. `key` is the data field to match and `value` the measure. Matching is forgiving: names, codes and aliases, ignoring accents, case and "Región de" ("RM", "Biobio", "XIII", "Aisén", "ARG", "EE.UU.", "TX", "Nueva York", "48").
|
|
161
|
+
|
|
162
|
+
**United States.** 50 states and DC in an Albers equal-area projection (`albersUsa`, the same composite as `d3.geoAlbersUsa`): Alaska (at 0.35×) and Hawaii sit in insets at the bottom left with a fine dashed frame; Puerto Rico gets its own inset only when the data includes it (`territories: true` forces it). Each state shows its postal code, plus the value when it fits; the small northeastern states (VT, NH, MA, RI, CT, NJ, DE, MD, DC) are labeled in a column on the right with leader lines. `values: false` keeps only the codes; `labels: false` removes them.
|
|
163
|
+
|
|
164
|
+
| Option | Meaning |
|
|
165
|
+
|---|---|
|
|
166
|
+
| `geo` | `"chile"`, `"usa"`, `"world"`, a name registered with `registerGeo()`, or a GeoJSON object. |
|
|
167
|
+
| `featureKey` | Feature property to match (default: tries code, id, ISO-3, name and aliases). |
|
|
168
|
+
| `scale`, `classes` | `"quantile"` (default), `"equal"`, `"continuous"` or a list of breaks; 5 classes by default. |
|
|
169
|
+
| `midpoint` / `diverging` | Diverging classes around a value (e.g. 0 for changes). |
|
|
170
|
+
| `missing` | `"plain"` drops the hatch and the "No data" legend entry. |
|
|
171
|
+
| `labels`, `bars` | Chile gets a north-to-south column with name, bar and value; `bars: false` hides the bars. World maps label the top N. |
|
|
172
|
+
| `projection` | `"mercator"`, `"equalEarth"` or `"albersUsa"` (picked automatically). |
|
|
173
|
+
| `dashed`, `dashedLabel` | Hatch some regions (preliminary figures, disputed areas): names, codes or a data field. They keep their class color. |
|
|
174
|
+
| `maxRadius` | `symbolmap`: largest circle radius. |
|
|
175
|
+
|
|
176
|
+
In the browser (`render()` or `toHTML()`), maps are interactive: hovering outlines the shape; clicking a region zooms to fit it (click again, double-click, Esc or the reset button to go back); Ctrl/⌘ + scroll zooms around the pointer (plain scroll keeps scrolling the page, as `chartjs-plugin-zoom` recommends with its `modifierKey`); pinch zooms on touch screens; drag pans when zoomed; +/− buttons work from the keyboard. Each color class in the legend is a key: hover it to light up its regions, click to pin. With `time`, every region's color transitions into the next period's instead of jumping, and the zoom is kept while the slider plays.
|
|
177
|
+
|
|
178
|
+
`lint` warns about rows that match no region or country. Sources: Natural Earth 1:110m (public domain) for the world; Biblioteca del Congreso Nacional for Chilean regions (attribution in `src/geo/data/chile-regiones.js`); U.S. Census Bureau cartographic boundaries 1:10m (public domain) via us-atlas 3.0.1 for US states. In the browser, `dist/plotagents-geo.js` adds the geometries; `toHTML()` includes it only when a page has a map.
|
|
179
|
+
|
|
180
|
+
## Dashed shapes: estimates, projections, provisional figures
|
|
181
|
+
|
|
182
|
+
Some numbers are not firm yet. Instead of a new color (which reads as a new category), plotagents keeps the shape's color and changes its texture: a hatched fill (stripes of the color over a light tint of it) and a dashed outline at 3:1 contrast. Lines get a dashed stroke with hollow points. It works in `bar`, `line`, `area`, `donut`/`pie`, `choropleth` and `symbolmap`.
|
|
183
|
+
|
|
184
|
+
| Option | Meaning |
|
|
185
|
+
|---|---|
|
|
186
|
+
| `dashed` | A category, series or region name, or a list (`["Texas", "Ohio"]`); a data field whose truthy rows are dashed (`"provisional"`, accepts `true`, `1`, `"sí"`, `"yes"`, `"x"`); `{ field, equals }`; or `true` for everything. |
|
|
187
|
+
| `dashedFrom` | On a time axis (`line`, `area`, `bar`): the first x that is projected. The line turns dashed from the last firm point, projected points are hollow, stacked areas are hatched in each series' color and a soft band with `dashedLabel` marks the stretch. |
|
|
188
|
+
| `dashedLabel` | What it means: "Projection", "Preliminary", "Estimate". It is the legend entry (hover it to light up only the dashed shapes), the band label and a line in the tooltip. Defaults to "Estimado"/"Estimate"; `lint` suggests setting it. |
|
|
189
|
+
|
|
190
|
+
This follows the Chart.js advice of using patterns (as `patternomaly` does) and `segment.borderDash` for projected stretches, so the distinction never depends on color alone.
|
|
191
|
+
|
|
192
|
+
## Networks and flows
|
|
193
|
+
|
|
194
|
+
### `network` (`graph`)
|
|
195
|
+
|
|
196
|
+
Edges in `data` (`from`, `to`, optional `value`), optional `nodes` with `id`, `group` and `size`. Deterministic force layout fitted to the whole area, or `layout: "circle"`. `labels` (number or list), `highlight` (a node and its neighbors), `focus` (a node id: it is pinned at the center and highlighted with its neighbors, all of them labeled; in the browser a click does it with a smooth transition, and a second click or Esc returns), `iterations`, `maxRadius`.
|
|
197
|
+
|
|
198
|
+
### `chord`
|
|
199
|
+
|
|
200
|
+
`from`, `to`, `value` rows, or `matrix` + `names`. `directed` (default false), `sort`, `ribbonColor: "smaller" | "larger" | "source"`, `labelOrientation: "radial"`.
|
|
201
|
+
|
|
202
|
+
### `flowchart` (`flow`, `diagram`)
|
|
203
|
+
|
|
204
|
+
`nodes` with `id`, `label`, `type` (`start`, `end`, `process`, `decision`, `data`, `note`), optional `lane` (swimlanes) and `group`; `edges` (or `data`) with `from`, `to` and an optional `label` ("Yes"/"No"). Layered layout with automatic direction (`direction: "TB" | "LR"`), orthogonal connectors with arrows, loops routed around the side. `nodeWidth`, `swimlanes: false`.
|
|
205
|
+
|
|
206
|
+
## Grids, flows and numbers
|
|
207
|
+
|
|
208
|
+
### `heatmap`
|
|
209
|
+
|
|
210
|
+
`x`, `y` categories and `value`. Sequential ramp by default; `midpoint` (or `diverging: true`) for a diverging ramp. Values are written in cells when they fit.
|
|
211
|
+
|
|
212
|
+
### `sankey`
|
|
213
|
+
|
|
214
|
+
`from`, `to`, `value` (also detects `source`/`target`). Color starts at the first stage with more than one node and flows downstream.
|
|
215
|
+
|
|
216
|
+
### `kpi`
|
|
217
|
+
|
|
218
|
+
`items: [{ label, value, delta, previous, period, good: "up" | "down", trend: [...], target, format }]`. Delta is a fraction (`0.12` → +12%); `good: "down"` for costs. Tiles reflow into balanced rows.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
plotagents turns one JSON object into a finished, accessible SVG chart. You pass rows of data and name the fields; the library makes every design decision: chart layout, colors, labels, number formats and spacing.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install github:carlosnavarreteliz/plotagents
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
plotagents is not on the npm registry yet, so install it from GitHub for now. Once it is published, `npm install plotagents` will work too. After installing, `npx plotagents` runs the CLI from your project.
|
|
12
|
+
|
|
13
|
+
Node 18 or later, Deno and Bun work out of the box. The package has no dependencies.
|
|
14
|
+
|
|
15
|
+
## Your first chart
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
import { plot } from 'plotagents';
|
|
19
|
+
|
|
20
|
+
const svg = plot({
|
|
21
|
+
type: 'bar',
|
|
22
|
+
title: 'Biobío accounts for 30% of southern sales',
|
|
23
|
+
subtitle: 'Sales by region, CLP millions, 2025',
|
|
24
|
+
source: 'illustrative data',
|
|
25
|
+
data: [
|
|
26
|
+
{ region: 'Biobío', sales: 1240 },
|
|
27
|
+
{ region: 'Araucanía', sales: 880 },
|
|
28
|
+
{ region: 'Ñuble', sales: 610 },
|
|
29
|
+
],
|
|
30
|
+
x: 'region',
|
|
31
|
+
y: 'sales',
|
|
32
|
+
locale: 'en',
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`svg` is a string. Write it to a `.svg` file, put it in HTML or Markdown, or convert it to PDF or PNG with any SVG tool.
|
|
37
|
+
|
|
38
|
+
## Anatomy of a spec
|
|
39
|
+
|
|
40
|
+
| Field | What it is |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `type` | The chart type, such as `bar`, `line` or `treemap`. Leave it out to infer it from the data. |
|
|
43
|
+
| `data` | A list of rows: `[{ region: "Biobío", sales: 1240 }]`. |
|
|
44
|
+
| `x`, `y`, `series` | Which field goes where. Each chart type documents its own roles in the [chart reference](charts.md). |
|
|
45
|
+
| `title` | The finding, as a full sentence with the key number. |
|
|
46
|
+
| `subtitle` | How to read the chart: measure, unit and period. |
|
|
47
|
+
| `source` | Where the data comes from. Use `"illustrative data"` when it is made up. |
|
|
48
|
+
| `locale` | `"es"` (the default, `1.234,5`) or `"en"` (`1,234.5`). |
|
|
49
|
+
|
|
50
|
+
### Wide tables
|
|
51
|
+
|
|
52
|
+
When each measure is its own column, pass a list in `y` and each field becomes a series:
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
plot({
|
|
56
|
+
type: 'line',
|
|
57
|
+
data: [
|
|
58
|
+
{ month: '2025-01', revenue: 120, costs: 90 },
|
|
59
|
+
{ month: '2025-02', revenue: 132, costs: 94 },
|
|
60
|
+
],
|
|
61
|
+
x: 'month',
|
|
62
|
+
y: ['revenue', 'costs'],
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Numbers written as text
|
|
67
|
+
|
|
68
|
+
Values such as `"1.234,5"`, `"12%"` or `"$ 3.000"` are parsed automatically, so data copied from a spreadsheet works without cleaning.
|
|
69
|
+
|
|
70
|
+
## In the browser
|
|
71
|
+
|
|
72
|
+
Without a bundler, load the browser build and call `render()`. It draws into an element and adds tooltips, responsive width, time sliders and drill-down.
|
|
73
|
+
|
|
74
|
+
```html
|
|
75
|
+
<div id="chart"></div>
|
|
76
|
+
<script src="https://cdn.jsdelivr.net/gh/carlosnavarreteliz/plotagents@master/dist/plotagents.js"></script>
|
|
77
|
+
<script>
|
|
78
|
+
plotagents.render('#chart', {
|
|
79
|
+
type: 'line',
|
|
80
|
+
data: [
|
|
81
|
+
{ month: '2025-01', sales: 120 },
|
|
82
|
+
{ month: '2025-02', sales: 140 },
|
|
83
|
+
{ month: '2025-03', sales: 135 },
|
|
84
|
+
],
|
|
85
|
+
x: 'month',
|
|
86
|
+
y: 'sales',
|
|
87
|
+
title: 'Sales rose 12% in the first quarter',
|
|
88
|
+
locale: 'en',
|
|
89
|
+
});
|
|
90
|
+
</script>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The same file ships in the package as `dist/plotagents.js`. Maps need the geometries too: add `dist/plotagents-geo.js` after `plotagents.js`.
|
|
94
|
+
|
|
95
|
+
`render()` returns a handle with `update(spec)` to redraw with a new spec and `destroy()` to remove the chart and its listeners.
|
|
96
|
+
|
|
97
|
+
## A whole report in one file
|
|
98
|
+
|
|
99
|
+
`toHTML()` builds a self-contained, interactive page with the library inlined, so it works offline and from a file.
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
import { writeFileSync } from 'node:fs';
|
|
103
|
+
import { toHTML } from 'plotagents';
|
|
104
|
+
|
|
105
|
+
writeFileSync('report.html', toHTML([spec1, spec2], { title: 'Monthly report', lang: 'en', columns: 2 }));
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## From the command line
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npx plotagents spec.json -o chart.svg # SVG
|
|
112
|
+
npx plotagents specs.json -o report.html # several interactive charts on one page
|
|
113
|
+
npx plotagents spec.json --lint # review and suggestions
|
|
114
|
+
npx plotagents spec.json --describe # alt text
|
|
115
|
+
npx plotagents --tool # tool definition for an agent
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The CLI also reads a spec from standard input: `cat spec.json | npx plotagents > chart.svg`.
|
|
119
|
+
|
|
120
|
+
## Catching mistakes
|
|
121
|
+
|
|
122
|
+
`plot()` throws a `PlotError` with a hint when a spec cannot be drawn. `safePlot()` never throws, and `lint()` reviews a spec without drawing it:
|
|
123
|
+
|
|
124
|
+
```js
|
|
125
|
+
import { safePlot } from 'plotagents';
|
|
126
|
+
|
|
127
|
+
const { ok, svg, errors, warnings, suggestions } = safePlot(spec);
|
|
128
|
+
if (!ok) console.log(errors.map((e) => `${e.message} → ${e.fix}`));
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Every error, warning and suggestion carries a `fix` that says what to change.
|
|
132
|
+
|
|
133
|
+
## Next steps
|
|
134
|
+
|
|
135
|
+
- Browse the [gallery](../examples/) to find the chart you need and copy its code.
|
|
136
|
+
- Try specs live in the [playground](../playground/).
|
|
137
|
+
- Read the [chart reference](charts.md) for every option of every type.
|
|
138
|
+
- Give an agent the [agent guide](agents.md) and the tool definition.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Rendering: SVG or Canvas?
|
|
2
|
+
|
|
3
|
+
**Decision: SVG everywhere, with automatic path batching for dense marks.** Canvas was evaluated and rejected: batched SVG is faster than Canvas on every size we tested and keeps the properties that matter for a library used by agents.
|
|
4
|
+
|
|
5
|
+
## What we measured
|
|
6
|
+
|
|
7
|
+
Scatter plot, two colors, 720 × 420 px, Chromium (Playwright), time from inserting the markup to the second animation frame. Same machine, same data.
|
|
8
|
+
|
|
9
|
+
| Points | SVG, one `<circle>` per point | Canvas 2D (`arc` + `fill`) | SVG, one `<path>` per color | plotagents (batched SVG, full chart) |
|
|
10
|
+
|---|---|---|---|---|
|
|
11
|
+
| 1,000 | 32 ms | 29 ms | 22 ms | 26 ms |
|
|
12
|
+
| 5,000 | 62 ms | 30 ms | 13 ms | 12 ms |
|
|
13
|
+
| 20,000 | 671 ms · 2.9 MB | 100 ms | 37 ms | 38 ms · 2.1 MB |
|
|
14
|
+
| 50,000 | 1,468 ms · 7.2 MB | 357 ms | 138 ms | 123 ms · 3.4 MB |
|
|
15
|
+
|
|
16
|
+
The cost of naive SVG is the DOM: one element per mark. Drawing all circles of one color as arcs inside a single `<path>` removes that cost, and the browser rasterizes one path faster than a script can issue tens of thousands of Canvas calls.
|
|
17
|
+
|
|
18
|
+
Reproduce it with `node scripts/bench.js` (needs Playwright with Chromium installed).
|
|
19
|
+
|
|
20
|
+
## Why SVG wins for this library
|
|
21
|
+
|
|
22
|
+
| Need | SVG | Canvas |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| Render on a server, in Node, with zero dependencies | Yes: SVG is a string | Needs a native module (`node-canvas`) or a headless browser |
|
|
25
|
+
| An agent can read and check its own output | Yes: titles, labels and values are text | No: a bitmap |
|
|
26
|
+
| Accessibility (`<title>`, `<desc>`, screen readers, text selection) | Built in | Must be rebuilt by hand next to the canvas |
|
|
27
|
+
| Crisp at any zoom, print and PDF | Vector | Fixed resolution; must re-render for each pixel ratio |
|
|
28
|
+
| Paste into slides, Word, Figma, Markdown, email | Works as a file | Only as an image |
|
|
29
|
+
| Theming with CSS, dark mode, hover states | CSS classes | Manual redraw |
|
|
30
|
+
| 50,000+ points | Fast with batching (above) | Fast |
|
|
31
|
+
|
|
32
|
+
Canvas only wins when a chart needs hundreds of thousands of marks redrawn every frame, such as live streaming or animated particle fields. That is outside what a chart for decisions needs, and well past the point where a heatmap or a density plot (`kde`, `hist`) tells the story better than overplotted points.
|
|
33
|
+
|
|
34
|
+
## How batching works
|
|
35
|
+
|
|
36
|
+
`dotLayer()` in `src/svg.js` takes the circles a chart wants to draw:
|
|
37
|
+
|
|
38
|
+
- **Up to 1,500 points:** one `<circle>` each, with its own tooltip and hover state.
|
|
39
|
+
- **More than 1,500 points:** circles are grouped by style (color, radius, opacity) and each group becomes one `<path>` of arcs. A transparent `<rect class="pa-near">` carries the coordinates and tooltip text of up to 20,000 points, and the browser layer finds the nearest point within 14 px on hover and rings it.
|
|
40
|
+
|
|
41
|
+
Scatter, bubble, regplot, residplot, jointplot, strip and swarm use it automatically. No option is needed.
|
|
42
|
+
|
|
43
|
+
## Other performance choices
|
|
44
|
+
|
|
45
|
+
- **Text measurement without a DOM.** Width tables calibrated for humanist sans faces let labels be fitted, wrapped and truncated identically in Node and in the browser.
|
|
46
|
+
- **Numbers rounded to two decimals** in all coordinates keeps files small.
|
|
47
|
+
- **One file for the browser.** `dist/plotagents.js` (no dependencies) is inlined by `toHTML()` so interactive pages work offline, from a file, or inside an artifact.
|
package/docs/themes.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Themes
|
|
2
|
+
|
|
3
|
+
A theme is a JSON object. Pass a name or an object; objects are deep-merged over a base theme, so you only write what changes.
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
plot({ ...spec, theme: 'noche' }); // built-in dark theme
|
|
7
|
+
plot({ ...spec, theme: { extends: 'noche', marks: { lineWidth: 3 } } });
|
|
8
|
+
plot({ ...spec, palette: 'okabe-ito' }); // shortcut for theme.categorical
|
|
9
|
+
plot({ ...spec, ramp: 'teal' }); // shortcut for theme.sequential
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Library-wide defaults
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
configure({ font: 'Poppins', textScale: 1.4, theme: { extends: 'papel', marks: { lineWidth: 2.5 } } });
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Every later `plot()`, `render()` and `toHTML()` starts from these values; the spec always wins. `configure()` returns the current defaults and `configure(null)` clears them.
|
|
19
|
+
|
|
20
|
+
## Built-in themes
|
|
21
|
+
|
|
22
|
+
| Name | Mode | Surface | Use |
|
|
23
|
+
|---|---|---|---|
|
|
24
|
+
| `papel` (alias `light`) | light | `#FCFBF9` paper | default |
|
|
25
|
+
| `noche` (alias `dark`) | dark | `#0B0F2B` deep navy | dark pages, slides |
|
|
26
|
+
|
|
27
|
+
Both share the same eight hues, stepped separately for each surface and validated on it.
|
|
28
|
+
|
|
29
|
+
## Keys
|
|
30
|
+
|
|
31
|
+
| Key | Type | Meaning |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| `extends` | `'papel' \| 'noche'` | Base to merge over (default `papel`, or `noche` when `mode: 'dark'`). |
|
|
34
|
+
| `mode` | `'light' \| 'dark'` | Affects tooltips and text-on-fill contrast. |
|
|
35
|
+
| `surface` | color | Chart background. Also used for 2 px gaps and rings around marks. |
|
|
36
|
+
| `ink`, `lede`, `muted` | color | Primary text, secondary text, axis and source text. |
|
|
37
|
+
| `grid`, `axis` | color | Gridlines and baselines (1 px, solid). |
|
|
38
|
+
| `tint`, `tint2` | color | Quiet fills: KPI tiles, slider tracks, meters. |
|
|
39
|
+
| `dim` | color | Muted marks when `highlight` is used. |
|
|
40
|
+
| `other` | color | The "Other" bucket and series past the 8th. |
|
|
41
|
+
| `categorical` | colors or palette name | Identity colors, assigned in this order. |
|
|
42
|
+
| `sequential` | colors or ramp name | Light → dark, for heatmaps. |
|
|
43
|
+
| `diverging` | colors or ramp name | Two poles with a neutral midpoint. |
|
|
44
|
+
| `status` | `{ good, warning, critical }` | KPI deltas and meters. Never used for series. |
|
|
45
|
+
| `font` | `{ family, mono, import }` | Font stack and optional Google Fonts URL (`fontImport: true` embeds it in the SVG). |
|
|
46
|
+
| `type` | `{ title, subtitle, label, axis, legend, source, titleWeight }` | Type sizes in px. Titles shrink automatically on narrow widths. |
|
|
47
|
+
| `marks` | `{ barRadius, barMax, barMaxH, lineWidth, pointRadius, gap, ring, areaOpacity, bandOpacity, dotOpacity }` | Mark geometry. |
|
|
48
|
+
| `axes` | `{ inside, grid }` | `inside: true` puts y labels on their gridlines inside the plot (more width for data). |
|
|
49
|
+
| `layout` | `{ padding, paddingSmall }` | Outer margin on wide and narrow widths. |
|
|
50
|
+
|
|
51
|
+
## Named palettes and ramps
|
|
52
|
+
|
|
53
|
+
| Palettes (`categorical`) | Ramps (`sequential` / `diverging`) |
|
|
54
|
+
|---|---|
|
|
55
|
+
| `cdia` (default), `cdia-noche`, `okabe-ito`, `tableau10`, `udec` | `indigo`, `magenta`, `teal`, `amber`, `indigo-magenta`, `teal-rust` |
|
|
56
|
+
|
|
57
|
+
## Validating a palette
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
import { validatePalette } from 'plotagents';
|
|
61
|
+
|
|
62
|
+
validatePalette(['#223C6A', '#E69B0A', '#D21428'], { mode: 'light', surface: '#FFFFFF', locale: 'en' });
|
|
63
|
+
// { ok: false, checks: [
|
|
64
|
+
// { name: 'lightness', status: 'fail', detail: 'Outside the lightness band 0.43–0.77: #223C6A' }, … ] }
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
| Check | Threshold | Why |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| Lightness band | OKLCH L 0.43–0.77 (light), 0.48–0.67 (dark) | Too light disappears, too dark reads as text. |
|
|
70
|
+
| Chroma floor | OKLCH C ≥ 0.10 | Below it a hue reads as gray. |
|
|
71
|
+
| Colorblind separation | ΔE OKLab × 100 ≥ 8 between neighbors, simulated protanopia and deuteranopia (Machado 2009) | Neighbors must stay apart for color-vision deficiencies. |
|
|
72
|
+
| Normal-vision separation | ΔE ≥ 15 | Neighbors must be easy to tell apart for everyone. |
|
|
73
|
+
| Contrast with the surface | ≥ 3:1, warning only | Below it, marks need visible labels. |
|
|
74
|
+
|
|
75
|
+
Use `pairs: 'all'` for scatter plots and maps, where any two colors can sit next to each other. The built-in palette passes all-pairs for its first three colors, which is why `lint` warns about scatter plots with more than three groups.
|
|
76
|
+
|
|
77
|
+
`lint(spec)` runs this automatically whenever a spec brings its own palette. Messages follow `locale` (Spanish by default, `'en'` for English).
|
|
78
|
+
|
|
79
|
+
## Color follows the entity
|
|
80
|
+
|
|
81
|
+
Colors are assigned to entities, never to positions:
|
|
82
|
+
|
|
83
|
+
- With `time`, colors are computed over all periods, so a group keeps its color while the slider moves even if its rank changes.
|
|
84
|
+
- With `focus` (drill-down), the subtree keeps its parent's color family.
|
|
85
|
+
- With `facet`, every panel shares one color map.
|
|
86
|
+
- `colors: { "Copper": "#B5562E" }` pins any entity to a color.
|