AI-First Data Visualization
Philosophy
Build production-quality interactive data interfaces with modern component libraries — no vendor lock-in, embeddable anywhere. When no tool is specified, build code-first. When the user explicitly names a BI tool, use it — only suggest code-first if they ask for options or hit a technical blocker.
Technology Stack
Full API patterns & code: references/component-guide.md
Framework Priority
- React + Tailwind — Default when JSX/TSX supported
- HTML + CSS + Vanilla JS — Fallback (use D3 or Chart.js)
- Python (Plotly/Dash) — Python-only environments only
Library Selection
| Library |
Best For |
| shadcn/ui charts |
Default first choice — general dashboards, most chart types |
| Recharts |
Line, bar, area, composed, radar — fine-grained control |
| Tremor |
KPI cards, metric displays, full dashboard layouts |
| Nivo |
Heatmaps, treemaps, choropleth, calendar, Sankey, funnel |
| visx |
Bespoke custom viz — D3-level control with React |
| D3.js |
Force-directed graphs, DAGs, maps — maximum flexibility |
| Victory |
When animation quality matters most |
Supporting: Tailwind CSS · Radix UI · Framer Motion · Lucide React · date-fns · Papaparse · lodash
Building a Visualization
Step 1: Understand the Data Story
Before code, identify: What question does the data answer? Who is the audience (exec → KPIs only, analyst → drill-down, public → narrative)? What's the ONE key insight? Design around it.
Step 2: Choose Chart Type
| Data Relationship |
Chart Type |
Library |
| Trend over time |
Line, Area |
shadcn/Recharts |
| Category comparison |
Bar (horizontal if many) |
shadcn/Recharts |
| Part of whole |
Donut, Treemap |
shadcn/Nivo |
| Distribution |
Histogram, Box, Violin |
Nivo/visx |
| Correlation |
Scatter, Bubble |
Recharts/visx |
| Geographic |
Choropleth, Dot map |
Nivo/D3 |
| Hierarchical |
Treemap, Sunburst |
Nivo |
| Flow / Process |
Sankey, Funnel |
Nivo/D3 |
| Single KPI |
Metric card, Gauge, Sparkline |
Tremor/shadcn |
| Multi-metric overview |
Dashboard grid of cards |
Tremor + shadcn |
| Ranking |
Horizontal bar, Bar list |
Tremor |
| Column/model lineage |
Force-directed DAG |
D3 |
| Pipeline dependencies |
Hierarchical tree, DAG |
D3/Nivo |
| Multi-dimensional quality |
Radar/Spider |
Recharts |
| Activity density over time |
Calendar heatmap |
Nivo |
| Incremental change breakdown |
Waterfall |
Recharts (custom) |
Step 3: Build the Interface
Start from this layout — remove what the data doesn't need:
┌─────────────────────────────────────────┐
│ Header: Title + Description + Date Range│
├─────────────────────────────────────────┤
│ KPI Row: 3-5 metric cards + sparklines │
├─────────────────────────────────────────┤
│ Primary Visualization (largest chart) │
├──────────────────┬──────────────────────┤
│ Secondary Chart │ Supporting Chart/Tbl │
├──────────────────┴──────────────────────┤
│ Detail Table (sortable, filterable) │
└─────────────────────────────────────────┘
A single insight might just be one chart with a headline and annotation. Scale complexity to audience.
Step 4: Design Principles
- Data-ink ratio: Remove chartjunk — unnecessary gridlines, redundant labels, decorative borders
- Color with purpose: Encode meaning (red=bad, green=good, blue=neutral). Max 5-7 colors. Single-hue gradient for sequential data
- Typography hierarchy: Title → subtitle (muted) → axis labels (small) → data labels
- Responsive:
min-h-[VALUE] on all charts. Grid stacks on mobile
- Animation: Entry transitions only,
duration-300 to duration-500. Never continuous
- Accessibility:
aria-label on charts, WCAG AA contrast, don't rely on color alone
- Dynamic color safety: When colors come from external sources (brand palettes, category maps, API data, user config), never apply them directly as text color without a contrast check. Dark colors are invisible on dark card backgrounds. Safe pattern: use the external color only for non-text elements (left border, dot, underline); always use the standard text color (white /
var(--text)) for the label itself. If color-coded text is required, apply a minimum lightness floor: color: hsl(from brandColor h s max(l, 60%))
- Icon semantics: Verify every icon matches its label's actual meaning, not just its visual shape. Common traps: using a rising-trend icon (📈) for metrics where lower is better (latency, error rate, cost); using achievement icons (🏆) for plain counts. When in doubt, use a neutral descriptive icon over a thematic one that could mislead
Step 5: Interactivity & Annotations
Priority: Tooltips (every chart) → Filtering → Sorting → Drill-down → Cross-filtering → Export → Annotations
Annotations turn charts into stories. Mark: inflection points, threshold crossings (amber), external events (indigo/red), anomalies (red), achievements (green). Limit 3 per chart. Implementation: references/component-guide.md → Annotation Patterns.
Step 6: Tell the Story
- Headline states insight: "Revenue grew 23% QoQ, driven by enterprise" — not "Q3 Revenue Chart"
- Annotate key moments directly on chart
- Contextual comparisons: vs. prior period, vs. target, vs. benchmark
- Progressive disclosure: Overview first — detail on demand
Environment-Specific Guidance
| Environment |
Approach |
| Claude Artifacts |
React (JSX), single file, default export. Available: recharts, lodash, d3, lucide-react, shadcn via @/components/ui/*, Tailwind |
| Claude Code / Terminal |
Vite + React + Tailwind. Add shadcn/ui + Recharts. Structure: src/components/charts/, src/components/cards/, src/data/ |
| Python / Jupyter |
Plotly for charts, Plotly Dash for dashboards |
| Cursor / Bolt / other IDEs |
Match existing framework. Prefer shadcn/ui if present |
Anti-Patterns
- Screenshot/static charts — build interactive components
- Defaulting to BI tools unprompted — build code-first when no tool specified
- Default matplotlib — always customize in Python
- Rainbow palettes — use deliberate, meaningful colors
- 3D charts — almost never appropriate
- Pie charts > 5 slices — use horizontal bar
- Unlabeled dual y-axes — use two separate charts
- Truncated bar axes — always start at zero
- Filtering or mapping over a field not confirmed to exist in the data export — an undefined field in
.filter() or .map() produces empty arrays or NaN silently, not an error; always validate the exported schema matches what the chart code consumes
Pre-Delivery Checklist
Before marking a dashboard complete:
1---2name: data-viz3description: Build modern, interactive data visualizations and dashboards using code-based component libraries (shadcn/ui, Recharts, Tremor, Nivo, D3, Victory, visx). Use this skill whenever the user asks to visualize data, build dashboards, create analytics views, chart metrics, tell a data story, build a reporting interface, create KPI cards, plot graphs, or explore a dataset — even if they mention PowerBI, Tableau, Streamlit, Metabase, Looker, Grafana, or similar tools. Also trigger when the user says "make a dashboard", "show me the data", "chart this", "visualize trends", "build an analytics page", "data story", or anything involving turning raw data into interactive visual interfaces. If the task involves presenting data visually — this is the skill. Always prefer building a real, interactive, code-based UI over exporting to or recommending a BI platform.4---56# AI-First Data Visualization78## Philosophy910Build production-quality interactive data interfaces with modern component libraries — no vendor lock-in, embeddable anywhere. When no tool is specified, build code-first. When the user explicitly names a BI tool, use it — only suggest code-first if they ask for options or hit a technical blocker.1112## Technology Stack1314Full API patterns & code: `references/component-guide.md`1516### Framework Priority17181. **React + Tailwind** — Default when JSX/TSX supported192. **HTML + CSS + Vanilla JS** — Fallback (use D3 or Chart.js)203. **Python (Plotly/Dash)** — Python-only environments only2122### Library Selection2324| Library | Best For |25|---------|----------|26| **shadcn/ui charts** | Default first choice — general dashboards, most chart types |27| **Recharts** | Line, bar, area, composed, radar — fine-grained control |28| **Tremor** | KPI cards, metric displays, full dashboard layouts |29| **Nivo** | Heatmaps, treemaps, choropleth, calendar, Sankey, funnel |30| **visx** | Bespoke custom viz — D3-level control with React |31| **D3.js** | Force-directed graphs, DAGs, maps — maximum flexibility |32| **Victory** | When animation quality matters most |3334**Supporting**: Tailwind CSS · Radix UI · Framer Motion · Lucide React · date-fns · Papaparse · lodash3536## Building a Visualization3738### Step 1: Understand the Data Story3940Before code, identify: **What question does the data answer?** Who is the audience (exec → KPIs only, analyst → drill-down, public → narrative)? **What's the ONE key insight?** Design around it.4142### Step 2: Choose Chart Type4344| Data Relationship | Chart Type | Library |45|---|---|---|46| Trend over time | Line, Area | shadcn/Recharts |47| Category comparison | Bar (horizontal if many) | shadcn/Recharts |48| Part of whole | Donut, Treemap | shadcn/Nivo |49| Distribution | Histogram, Box, Violin | Nivo/visx |50| Correlation | Scatter, Bubble | Recharts/visx |51| Geographic | Choropleth, Dot map | Nivo/D3 |52| Hierarchical | Treemap, Sunburst | Nivo |53| Flow / Process | Sankey, Funnel | Nivo/D3 |54| Single KPI | Metric card, Gauge, Sparkline | Tremor/shadcn |55| Multi-metric overview | Dashboard grid of cards | Tremor + shadcn |56| Ranking | Horizontal bar, Bar list | Tremor |57| Column/model lineage | Force-directed DAG | D3 |58| Pipeline dependencies | Hierarchical tree, DAG | D3/Nivo |59| Multi-dimensional quality | Radar/Spider | Recharts |60| Activity density over time | Calendar heatmap | Nivo |61| Incremental change breakdown | Waterfall | Recharts (custom) |6263### Step 3: Build the Interface6465Start from this layout — remove what the data doesn't need:6667```68┌─────────────────────────────────────────┐69│ Header: Title + Description + Date Range│70├─────────────────────────────────────────┤71│ KPI Row: 3-5 metric cards + sparklines │72├─────────────────────────────────────────┤73│ Primary Visualization (largest chart) │74├──────────────────┬──────────────────────┤75│ Secondary Chart │ Supporting Chart/Tbl │76├──────────────────┴──────────────────────┤77│ Detail Table (sortable, filterable) │78└─────────────────────────────────────────┘79```8081A single insight might just be one chart with a headline and annotation. Scale complexity to audience.8283### Step 4: Design Principles8485- **Data-ink ratio**: Remove chartjunk — unnecessary gridlines, redundant labels, decorative borders86- **Color with purpose**: Encode meaning (red=bad, green=good, blue=neutral). Max 5-7 colors. Single-hue gradient for sequential data87- **Typography hierarchy**: Title → subtitle (muted) → axis labels (small) → data labels88- **Responsive**: `min-h-[VALUE]` on all charts. Grid stacks on mobile89- **Animation**: Entry transitions only, `duration-300` to `duration-500`. Never continuous90- **Accessibility**: `aria-label` on charts, WCAG AA contrast, don't rely on color alone91- **Dynamic color safety**: When colors come from external sources (brand palettes, category maps, API data, user config), never apply them directly as text color without a contrast check. Dark colors are invisible on dark card backgrounds. Safe pattern: use the external color only for non-text elements (left border, dot, underline); always use the standard text color (white / `var(--text)`) for the label itself. If color-coded text is required, apply a minimum lightness floor: `color: hsl(from brandColor h s max(l, 60%))`92- **Icon semantics**: Verify every icon matches its label's actual meaning, not just its visual shape. Common traps: using a rising-trend icon (📈) for metrics where lower is better (latency, error rate, cost); using achievement icons (🏆) for plain counts. When in doubt, use a neutral descriptive icon over a thematic one that could mislead9394### Step 5: Interactivity & Annotations9596**Priority**: Tooltips (every chart) → Filtering → Sorting → Drill-down → Cross-filtering → Export → Annotations9798**Annotations** turn charts into stories. Mark: inflection points, threshold crossings (amber), external events (indigo/red), anomalies (red), achievements (green). **Limit 3 per chart.** Implementation: `references/component-guide.md` → Annotation Patterns.99100### Step 6: Tell the Story101102- **Headline states insight**: "Revenue grew 23% QoQ, driven by enterprise" — not "Q3 Revenue Chart"103- **Annotate key moments** directly on chart104- **Contextual comparisons**: vs. prior period, vs. target, vs. benchmark105- **Progressive disclosure**: Overview first — detail on demand106107## Environment-Specific Guidance108109| Environment | Approach |110|---|---|111| **Claude Artifacts** | React (JSX), single file, default export. Available: `recharts`, `lodash`, `d3`, `lucide-react`, shadcn via `@/components/ui/*`, Tailwind |112| **Claude Code / Terminal** | Vite + React + Tailwind. Add shadcn/ui + Recharts. Structure: `src/components/charts/`, `src/components/cards/`, `src/data/` |113| **Python / Jupyter** | Plotly for charts, Plotly Dash for dashboards |114| **Cursor / Bolt / other IDEs** | Match existing framework. Prefer shadcn/ui if present |115116## Anti-Patterns117118- Screenshot/static charts — build interactive components119- Defaulting to BI tools unprompted — build code-first when no tool specified120- Default matplotlib — always customize in Python121- Rainbow palettes — use deliberate, meaningful colors122- 3D charts — almost never appropriate123- Pie charts > 5 slices — use horizontal bar124- Unlabeled dual y-axes — use two separate charts125- Truncated bar axes — always start at zero126- Filtering or mapping over a field not confirmed to exist in the data export — an undefined field in `.filter()` or `.map()` produces empty arrays or NaN silently, not an error; always validate the exported schema matches what the chart code consumes127128## Pre-Delivery Checklist129130Before marking a dashboard complete:131132- [ ] Every tab / view activated — all charts render (no blank canvases, no unexpected 0–1 axes)133- [ ] Every field referenced in chart/filter code confirmed present in the data export134- [ ] All text readable on its background — check explicitly when colors come from external data135- [ ] All icons match their label's meaning136- [ ] Tooltips appear on hover for every chart137- [ ] No chart silently receives an empty dataset — add a visible empty state or console warning138- [ ] Mobile: grid stacks correctly, no body-level horizontal overflow