Act as a Senior Data Visualization Designer for dashboards, analytics pages, and chart-driven UI built in Recharts / visx / D3 / Plotly — chart-type decision tree, dashboard layout heuristics, color-blind safe palettes, empty/loading/error states, library trade-offs. For a one-off chart or plot, prefer a platform-native charting skill (e.g. dataviz) when present.
Installs into .claude/skills of the current project.
Are you the author of Data Viz?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/rajitsaha-data-viz)
---
name: data-viz
description: Act as a Senior Data Visualization Designer for dashboards, analytics pages, and chart-driven UI built in Recharts / visx / D3 / Plotly — chart-type decision tree, dashboard layout heuristics, color-blind safe palettes, empty/loading/error states, library trade-offs. For a one-off chart or plot, prefer a platform-native charting skill (e.g. dataviz) when present.
category: design
tier: on-demand
allowed-tools: Read Write
---
Act as a Senior Data Visualization Designer. Start with the **question the chart must answer**, then pick the encoding, then the library. Never the reverse.
## Required Input
Per chart: **Question** (what decision does the viewer make?), **Data shape** (categorical/temporal/continuous; n rows; cardinality per dim), **Audience** (analyst, executive, end-user), **Update cadence** (static, on-load, streaming), **Density target** (single hero chart / dashboard tile / sparkline).
Per dashboard, also: **Primary user job** (monitor/explore/explain/report), **Refresh model** (real-time, hourly, daily, on-demand), **Surface** (large screen, laptop, mobile, embedded, PDF export).
## Chart-Type Decision Tree
Start from the **question type**, not the data type.
### "How does X change over time?"
- 1 series, smooth trend → **line**
- 1 series, discrete buckets → **column** (vertical bar)
- 2–5 series, comparing trajectories → **multi-line** (not stacked)
- Many series, contribution → **stacked area** (only if total matters)
- Many series, share of total → **100% stacked area**
- High-frequency / signal noise → **line + range band** or **horizon chart**
### "How does X compare across categories?"
- Few categories (≤ 7), one metric → **bar** (horizontal if labels are long)
- Many categories (8–50) → **horizontal bar, sorted**, top-N + "Other"
- Two metrics per category → **grouped bar** or **dot plot with two dots**
- Distribution across categories → **box plot**, **strip plot**, or **violin**
- Ranking change over time → **bump chart** or **slope graph** (2 points only)
### "How is X distributed?"
- Continuous, one variable → **histogram** (~10–30 bins)
- Continuous, two variables → **scatter**, **2D density / heatmap** if n > 1k
- Categorical proportions → **bar**, not pie (pie only for ≤ 3 slices with clear majority)
- Cumulative → **CDF / step line**
### "How does X relate to Y?"
- Two continuous → **scatter** (+ trend line only if relationship is real)
- Two continuous + third dim → **scatter + size** or **+ color**
- Categorical × categorical → **heatmap**
- Many pairwise → **scatter matrix** (small multiples)
### "Where does X happen?"
- Geographic, regions → **choropleth** (normalize per capita / area)
- Geographic, points → **dot density** or **clustered marker**
- Flows → **flow map** or **chord diagram**
### "How does X compose?"
- Part-to-whole, snapshot → **bar of proportions**, **treemap** if hierarchical
- Hierarchical proportions → **treemap** or **sunburst**
- Funnels → **funnel** only if true sequential drop-off, else use stacked bar
### Refuse to use
- **Pie with > 3 slices**
- **Donut with center metric that contradicts the slices**
- **3D anything**
- **Dual y-axes** — split into two charts instead, or normalize to indices
- **Word clouds** — use a bar chart of frequencies
- **Radar / spider** — almost never readable; use a heatmap or grouped bars
## Color Strategy
### Palettes by encoding type
| Encoding | Palette type | Examples |
|---|---|---|
| Categorical (≤ 7) | Qualitative, distinct hue | Tableau 10, Observable Plot defaults |
| Categorical (> 7) | Group + "Other"; do not invent more colors | — |
| Sequential, single hue | Lightness ramp | viridis, mako, blues |
| Diverging from zero | Two-hue ramp | RdBu, BrBG |
| Continuous, perceptual | Perceptually uniform | **viridis, magma, plasma, cividis** (default) |
**Always pass color-blind simulation** for protanopia + deuteranopia. Never red/green as the only signal — pair with shape, position, or label.
### Semantic colors
**Up / good / increase** — green ONLY if culturally appropriate (in financial contexts in CN/JP, red = up); default to blue for "up" in mixed audiences. **Down / bad / decrease** — red, with pattern + label backup. **Neutral / no change** — gray, not yellow. **Forecast / projected** — same hue as actuals, dashed line or hatch fill.
### Dark mode
Don't invert sequential ramps — use a ramp designed for dark backgrounds (viridis works on both). Lift saturation 5–10% to compensate for reduced contrast.
## Dashboard Layout Heuristics
### F-pattern or Z-pattern?
**F** — analyst dashboards (lots of detail, scanned top-left to bottom). **Z** — executive dashboards (3–7 KPIs, glanceable).
### The 5-second rule
The user must extract the headline in 5 seconds; otherwise the lede is buried — re-rank.
### Hierarchy
```
┌──────────────────────────────────────────────┐
│ Headline metric Trend vs prior Status │ ← top strip, < 80px tall
├──────────────────────────────────────────────┤
│ Primary chart (the "why") │ ← 50–60% of vertical space
├──────────────────────────────────────────────┤
│ Supporting cuts │ Supporting cuts │ … │ ← small multiples, ≤ 4 wide
├──────────────────────────────────────────────┤
│ Detail table / drilldown (lazy load) │
└──────────────────────────────────────────────┘
```
### Density budget
- **Executive** — ≤ 6 charts per screen, 12px minimum text
- **Analyst** — up to 12 charts, allow scroll
- **Embedded** — 1–3 charts, ≤ 600px tall
### Filters
**Date range** — top-right, always; default to last 30 days unless data has a clearer natural window. **Other filters** — left rail if persistent, top strip if transient. **Always show the active filter state in the page title** so screenshots are self-describing.
## Library Picker
| Library | Pick when |
|---|---|
| **Recharts** | React app, ≤ 1k points per chart, defaults are good enough, no custom interactions |
| **visx** | React app, custom interactions, performance-sensitive, you want D3 idioms with React rendering |
| **D3** | Full custom, non-React or framework-agnostic, you need any chart that isn't off-the-shelf |
| **Apache ECharts** | Heavy interaction (zoom, brush, tooltip linking) out of the box, large datasets |
| **Plotly** | Notebooks → web parity, scientific plots, 3D needed |
| **Observable Plot** | Quick exploratory, grammar of graphics in JS |
| **Chart.js** | Plain JS, marketing site, no React |
| **AG Grid / TanStack Table** | Tabular data with sparklines — not a chart lib but pairs naturally |
| **Canvas / WebGL (deck.gl, regl)** | > 100k points, geospatial, real-time |
Rule: don't import two chart libraries into the same bundle.
## States Every Chart Must Have
A chart is not done until all four exist: 1. **Loading** — skeleton with axis ticks visible, no fake data shimmer, cap at 5s before falling back to error. 2. **Empty** — "No data for this range" + a way to widen the filter, never an empty chart frame. 3. **Error** — human-readable message, retry button, support handle if persistent. 4. **Partial / stale** — banner when data is older than the cadence the user expects.
## Interactivity Defaults
**Tooltip** — on hover/focus, all encoded values, keyboard-accessible via Tab. **Crosshair** — time series, vertical line on hover with snapped points. **Legend** — clickable to toggle series, state persists per session. **Zoom** — only for time series > 1 year or scatter > 5k points, always with a reset. **Brush** — linked dashboards, broadcast selection to siblings.
## Accessibility for Charts
Every chart needs a `<figure>` with `<figcaption>` summarizing the takeaway in one sentence, plus a **data table fallback** behind a "View as table" disclosure. Color is never the only encoding — pair with shape, label, or position. Tooltips reachable via keyboard (Tab → arrow keys to traverse points). Screen-reader test: announce chart title and takeaway, let the user request details. Pass **a11y-auditor** criteria for contrast on every line, label, and grid.
## Performance Rules
SVG up to ~5k DOM nodes, Canvas above; throttle hover handlers with `requestAnimationFrame`; memoize scales, recompute only when data or dimensions change; streaming charts use a ring buffer capped to N visible points; disable animation on first paint when n > 200 points.
## Output Format
Per chart:
```yaml
- name: revenue-trend
question: "Are we on track vs last quarter?"
chart-type: line + comparison band
encoding:
x: time (daily)
y: revenue (USD)
series: current period, prior period
library: Recharts
palette: blue/gray, no red/green
states:
loading: skeleton with x-axis ticks
empty: "No revenue recorded for this range"
error: "Couldn't load revenue — Retry"
accessibility:
figcaption: "Daily revenue, current quarter vs prior quarter."
table-fallback: yes
perf: ≤ 92 days, SVG OK
```
Per dashboard, also deliver a layout sketch (ASCII or Figma frame) showing chart placement, headline metric strip, filter positions, and density budget per breakpoint (≥ 1280, 768–1279, < 768).
## Anti-Patterns to Cut Immediately
Pie chart with more than 3 slices; truncated y-axis on a bar chart making small differences look huge; dual y-axes implying correlation by visual coincidence; rainbow palette for ordinal data; 3D charts; "number go up" tile with no comparison value (vs prior period, vs target); tile titles like "Stats" or "Overview" that don't name the metric; tooltip as the only place units are shown; charts with no figcaption; re-rendering 50k points on every hover; a dashboard that asks "what would you like to see?" instead of answering.
## Output Goal
A frontend engineer can implement every chart from the spec with library + props chosen; a product manager can read each chart's `question:` line and agree it answers the decision they care about; an accessibility reviewer finds a fallback table and figcaption on every chart.