Tufte Visualization Ideation (PMX Canvas)
Apply Edward Tufte's principles to design clear, honest, high-density data visualizations, then
realize them with PMX Canvas graph / json-render nodes. Color must encode data, not decorate.
Workflow
For new visualizations:
Clarify the data story
- What comparisons matter?
- What's the key insight to communicate?
- Who's the audience?
Select approach using Tufte principles:
- High comparison need → Small multiples (several small
graph nodes, shared scale)
- Dense data → Consider data tables (
json-render Table), sparklines (Sparkline)
- Time-series → Line charts with minimal grid
- Part-to-whole → Avoid pie charts; prefer bar/table
- Ranked single metric across categories → DotPlot over a bar forest
Design with data-ink in mind
- Start minimal, add only what's necessary
- Every element must earn its ink
- Default to a single accent; use the full palette only when color encodes a variable
Apply the eraser test before shipping
- For every element (label, tick, gridline, border, annotation): can it be erased without losing
information that's not already conveyed elsewhere?
- Watch for duplicate encodings: numeric labels next to a value already marked by a tick; legends
duplicating direct labels; per-panel scale annotations duplicating a shared-scale caption.
- If two elements compete for the same job, keep the visual one and drop the textual one (or vice
versa) - not both.
Apply the collision test before shipping
- For every text element in the plot (axis labels, point annotations, epoch labels, baseline
labels, explanatory notes): mentally draw its bounding box. Does anything else - another text
element, a data line, dense markers - live in or cross that box?
- The eraser test catches redundant elements; the collision test catches crowded ones. Both
must pass.
- Standard fixes: move explanatory prose out of the plot into a nearby markdown node; relocate
band/epoch labels to a dedicated strip above the plot; push baseline/reference labels to the
outside margin; give each in-plot annotation a leader line so the marker and the text occupy
clearly separated space.
- Watch especially: inverted axes; shared-scale small multiples (labels stacked near zero in every
panel); dense scatter (text vanishes into the dot cloud unless explicitly cleared).
Apply the Tufte test (see references/tufte-principles.md)
For critiquing visualizations:
Check graphical integrity
- Calculate lie factor if proportions seem off
- Verify baselines and scales (bar and area charts must start at zero)
- Look for 3D distortion
Identify chartjunk
- Decorative elements
- Heavy grids
- Unnecessary 3D effects
- Moiré patterns
- Gratuitous per-category color on a single-series chart (decoration, not encoding)
Evaluate data-ink ratio
- What can be erased?
- What's redundant?
Suggest improvements with specific before/after recommendations
Mapping to the PMX Canvas chart catalog
Realize these designs with canvas_render { action: "add-graph" } (graph nodes) and
canvas_render { action: "add-json-render" }.
The chart catalog: LineChart, BarChart, PieChart, AreaChart, ScatterChart, RadarChart,
StackedBarChart, ComposedChart, plus the Tufte primitives Sparkline, DotPlot, BulletChart,
and Slopegraph.
Color must encode data — the colorBy decision (single-series bar/column)
A single-series BarChart measures one variable across categories. Coloring each bar differently
encodes nothing — it is decoration (chartjunk). Use the colorBy prop:
colorBy |
When to use |
series (default) |
One accent for all bars, one bar highlighted (Tufte-safe emphasis). Use to draw the eye to the bar that matters (max, target, the row under discussion). |
category |
Opt in only when the category itself is a nominal variable the reader must map by color (e.g. team identity reused across several charts with a shared key). |
value |
Sequential shade by magnitude. Note this double-encodes — the bar's length already encodes the value — so reserve it for when the lightness ramp genuinely aids reading a ranked magnitude; otherwise series/none are more honest. |
none |
Flat single accent, no highlight. Maximal data-ink for dense small multiples. |
Default to series. Do not reach for category to "make it colorful." Pie/radar/stacked-bar
already rotate the palette because each slice/series is a distinct variable — leave those as-is.
Tufte primitives (prefer over heavier charts)
Sparkline — word-sized time-series, no axes/labels. Use inline in tables/dashboards and one
per row to show a trajectory at a glance. Replaces "trending up / volatile" prose with the shape.
DotPlot — ranked single metric across categories. Replaces a forest of bars: a dot per
category on a shared axis. Far higher data-ink ratio than bars; sorts make the macro pattern pop.
BulletChart — a measure against a target with qualitative bands. Replaces a gauge/dial
(which is chartjunk). Use for KPI-vs-target, progress-vs-goal.
Slopegraph — two-time-point comparison across many categories (before/after). Direct slope
encodes change and rank simultaneously; labels sit at the endpoints (direct labeling, no legend).
Lines default to a single neutral ink; set colorByDirection to accent rising lines and mute
falling ones only when the direction is the point (and beware it editorializes — a falling
error-rate is "good", a falling revenue is "bad").
Direct labeling over legends
Legends force the eye to ping-pong between key and plot (a duplicate encoding). Prefer labeling the
data directly: end-of-line labels on LineChart/Slopegraph, endpoint labels on DotPlot, the
highlighted bar's value on BarChart. Set showLegend: false on graph nodes when one or two series
are directly identifiable; reserve legends for genuinely many overlapping series.
Small multiples over many overlapping series
When more than ~4 series would overlap in one chart, do not cram them into a single multi-color
LineChart. Create several small graph nodes with an identical shared scale and consistent
encoding, arranged in a grid (canvas_view { action: "arrange", layout: "grid" }, or a group).
Position means the same thing in every panel; the sequence tells the macro story while each panel
carries the micro detail. This is almost always better than color-coding 6+ lines.
Key Principles Reference
references/tufte-principles.md - core principles from Visual Display of Quantitative Information:
lie factor, data-ink, chartjunk, small multiples, integrity.
references/analytical-design.md - extensions from Envisioning Information, Visual Explanations,
and Beautiful Evidence: the 6 principles of analytical design, sparklines, layering & separation,
micro/macro, range-frames, causality, confections. Load when designing dashboards, dense displays,
sparklines, or explanatory graphics.
Quick checklist:
1---2name: tufte-viz3description: Ideate and critique data visualizations using Edward Tufte's principles, and map them onto the PMX Canvas json-render chart catalog (graph / json-render nodes). Use this skill when: (1) Designing or critiquing a canvas graph/json-render chart (2) Choosing a chart type, color encoding (colorBy), or primitive (Sparkline, DotPlot, BulletChart, Slopegraph) (3) Reviewing a board's dashboards/charts for graphical integrity and data-ink (4) Deciding between a single-series bar, small multiples, or direct labeling (5) Reducing chartjunk or improving data-ink ratio on canvas charts Applies: data-ink ratio, chartjunk elimination, graphical integrity, lie factor, small multiples, data density — and the canvas colorBy decision (color must encode data, not decorate).4---56# Tufte Visualization Ideation (PMX Canvas)78Apply Edward Tufte's principles to design clear, honest, high-density data visualizations, then9realize them with PMX Canvas `graph` / `json-render` nodes. Color must encode data, not decorate.1011## Workflow1213### For new visualizations:14151. **Clarify the data story**16 - What comparisons matter?17 - What's the key insight to communicate?18 - Who's the audience?19202. **Select approach** using Tufte principles:21 - High comparison need → Small multiples (several small `graph` nodes, shared scale)22 - Dense data → Consider data tables (`json-render` Table), sparklines (`Sparkline`)23 - Time-series → Line charts with minimal grid24 - Part-to-whole → Avoid pie charts; prefer bar/table25 - Ranked single metric across categories → DotPlot over a bar forest26273. **Design with data-ink in mind**28 - Start minimal, add only what's necessary29 - Every element must earn its ink30 - Default to a single accent; use the full palette only when color *encodes* a variable31324. **Apply the eraser test before shipping**33 - For every element (label, tick, gridline, border, annotation): can it be erased without losing34 information that's not already conveyed elsewhere?35 - Watch for duplicate encodings: numeric labels next to a value already marked by a tick; legends36 duplicating direct labels; per-panel scale annotations duplicating a shared-scale caption.37 - If two elements compete for the same job, keep the visual one and drop the textual one (or vice38 versa) - not both.39405. **Apply the collision test before shipping**41 - For every text element in the plot (axis labels, point annotations, epoch labels, baseline42 labels, explanatory notes): mentally draw its bounding box. Does anything else - another text43 element, a data line, dense markers - live in or cross that box?44 - The eraser test catches *redundant* elements; the collision test catches *crowded* ones. Both45 must pass.46 - Standard fixes: move explanatory prose out of the plot into a nearby markdown node; relocate47 band/epoch labels to a dedicated strip above the plot; push baseline/reference labels to the48 outside margin; give each in-plot annotation a leader line so the marker and the text occupy49 clearly separated space.50 - Watch especially: inverted axes; shared-scale small multiples (labels stacked near zero in every51 panel); dense scatter (text vanishes into the dot cloud unless explicitly cleared).52536. **Apply the Tufte test** (see references/tufte-principles.md)5455### For critiquing visualizations:56571. **Check graphical integrity**58 - Calculate lie factor if proportions seem off59 - Verify baselines and scales (bar and area charts must start at zero)60 - Look for 3D distortion61622. **Identify chartjunk**63 - Decorative elements64 - Heavy grids65 - Unnecessary 3D effects66 - Moiré patterns67 - Gratuitous per-category color on a single-series chart (decoration, not encoding)68693. **Evaluate data-ink ratio**70 - What can be erased?71 - What's redundant?72734. **Suggest improvements** with specific before/after recommendations7475## Mapping to the PMX Canvas chart catalog7677Realize these designs with `canvas_render { action: "add-graph" }` (graph nodes) and78`canvas_render { action: "add-json-render" }`.79The chart catalog: `LineChart`, `BarChart`, `PieChart`, `AreaChart`, `ScatterChart`, `RadarChart`,80`StackedBarChart`, `ComposedChart`, plus the Tufte primitives `Sparkline`, `DotPlot`, `BulletChart`,81and `Slopegraph`.8283### Color must encode data — the `colorBy` decision (single-series bar/column)8485A single-series `BarChart` measures **one** variable across categories. Coloring each bar differently86encodes nothing — it is decoration (chartjunk). Use the `colorBy` prop:8788| `colorBy` | When to use |89|-------------|-----------------------------------------------------------------------------|90| `series` (default) | One accent for all bars, one bar highlighted (Tufte-safe emphasis). Use to draw the eye to the bar that *matters* (max, target, the row under discussion). |91| `category` | Opt in only when the category itself is a nominal variable the reader must map by color (e.g. team identity reused across several charts with a shared key). |92| `value` | Sequential shade by magnitude. Note this **double-encodes** — the bar's length already encodes the value — so reserve it for when the lightness ramp genuinely aids reading a ranked magnitude; otherwise `series`/`none` are more honest. |93| `none` | Flat single accent, no highlight. Maximal data-ink for dense small multiples. |9495Default to `series`. Do **not** reach for `category` to "make it colorful." Pie/radar/stacked-bar96already rotate the palette because each slice/series **is** a distinct variable — leave those as-is.9798### Tufte primitives (prefer over heavier charts)99100- **`Sparkline`** — word-sized time-series, no axes/labels. Use inline in tables/dashboards and one101 per row to show a trajectory at a glance. Replaces "trending up / volatile" prose with the shape.102- **`DotPlot`** — ranked single metric across categories. Replaces a forest of bars: a dot per103 category on a shared axis. Far higher data-ink ratio than bars; sorts make the macro pattern pop.104- **`BulletChart`** — a measure against a target with qualitative bands. Replaces a gauge/dial105 (which is chartjunk). Use for KPI-vs-target, progress-vs-goal.106- **`Slopegraph`** — two-time-point comparison across many categories (before/after). Direct slope107 encodes change and rank simultaneously; labels sit at the endpoints (direct labeling, no legend).108 Lines default to a single neutral ink; set `colorByDirection` to accent rising lines and mute109 falling ones only when the direction is the point (and beware it editorializes — a falling110 error-rate is "good", a falling revenue is "bad").111112### Direct labeling over legends113114Legends force the eye to ping-pong between key and plot (a duplicate encoding). Prefer labeling the115data directly: end-of-line labels on `LineChart`/`Slopegraph`, endpoint labels on `DotPlot`, the116highlighted bar's value on `BarChart`. Set `showLegend: false` on graph nodes when one or two series117are directly identifiable; reserve legends for genuinely many overlapping series.118119### Small multiples over many overlapping series120121When more than ~4 series would overlap in one chart, do **not** cram them into a single multi-color122`LineChart`. Create several small `graph` nodes with an **identical shared scale** and consistent123encoding, arranged in a grid (`canvas_view { action: "arrange", layout: "grid" }`, or a `group`).124Position means the same thing in every panel; the sequence tells the macro story while each panel125carries the micro detail. This is almost always better than color-coding 6+ lines.126127## Key Principles Reference128129- `references/tufte-principles.md` - core principles from *Visual Display of Quantitative Information*:130 lie factor, data-ink, chartjunk, small multiples, integrity.131- `references/analytical-design.md` - extensions from *Envisioning Information*, *Visual Explanations*,132 and *Beautiful Evidence*: the 6 principles of analytical design, sparklines, layering & separation,133 micro/macro, range-frames, causality, confections. Load when designing dashboards, dense displays,134 sparklines, or explanatory graphics.135136**Quick checklist:**137- [ ] Lie Factor ≈ 1.0 (no visual distortion; bars and areas start at zero)138- [ ] Maximum data-ink ratio139- [ ] Zero chartjunk (no per-category color unless color encodes a variable)140- [ ] `colorBy` chosen deliberately — default `series` (single accent + one highlight); avoid `value` unless the magnitude ramp earns the double-encode141- [ ] Clear labeling, direct over legend142- [ ] Answers "compared to what?"143- [ ] Shows causality or mechanism where relevant144- [ ] Multivariate (not over-reduced)145- [ ] Words, numbers, images integrated - not segregated146- [ ] Reveals multiple levels of detail (micro + macro)147- [ ] Layering: primary data dominates, secondary recedes148- [ ] Appropriate data density — Sparkline/DotPlot considered before a heavier chart149- [ ] >4 overlapping series → small multiples, not one rainbow chart