State-of-the-art data visualization for React/Next.js/TypeScript with Tailwind CSS. Creates compelling, tested, and accessible visualizations following Tufte principles and NYT Graphics standards. Activate on "data viz", "chart", "graph", "visualization", "dashboard", "plot", "Recharts", "Nivo", "D3". NOT for static images, print graphics, or basic HTML tables.
npx skills add https://github.com/curiositech/some_claude_skills --skill data-viz-2025
Create visualizations that Seaborn users, Tufte readers, and everyone else will love. Marry NYT Graphics rigor with MoMA aesthetics, Nike energy, and On Kawara precision.
✅ Use for:
❌ NOT for:
Every visual element must earn its place. Remove chart junk, maximize signal-to-noise.
Visualizations are art. Use spring physics, thoughtful color, and premium design systems.
Data representation must be honest. Test rigorously, document assumptions, preserve context.
What are you building?
├─ Exploratory analysis / many iterations
│ └─ → Observable Plot (grammar-of-graphics)
│
├─ Standard business charts (bars, lines, pies)
│ ├─ Simple React integration needed
│ │ └─ → Recharts (easiest, most popular)
│ └─ Premium aesthetics + theming
│ └─ → Nivo (beautiful out of the box)
│
├─ Custom, one-of-a-kind visualizations
│ ├─ Need low-level control
│ │ └─ → Visx (React + D3 primitives)
│ └─ Full D3 power
│ └─ → D3.js directly (steeper learning curve)
│
└─ Dashboard with Tailwind design system
├─ → Tremor (purpose-built for dashboards)
└─ → shadcn-ui Charts (Recharts + shadcn styling)
{
"dependencies": {
"@observablehq/plot": "^0.6.0", // Exploratory, grammar-of-graphics
"recharts": "^2.12.0", // React charts, simple & popular
"@nivo/core": "^0.87.0", // Beautiful, themeable charts
"@visx/visx": "^3.10.0", // Low-level D3 + React primitives
"d3": "^7.9.0", // Direct D3 for custom work
"@tremor/react": "^3.15.0", // Tailwind dashboard components
"framer-motion": "^11.0.0" // Smooth animations
},
"devDependencies": {
"@percy/cli": "^1.29.0", // Visual regression testing
"@testing-library/react": "^14.2.0", // Component testing
"@storybook/react": "^7.6.0" // Component playground
}
}
Observable Plot - You want ggplot2/Vega-Lite in JavaScript
Recharts - You want it to "just work" in React
<Component />)Nivo - You want visually stunning results
Visx - You want maximum control with React patterns
D3.js - You want unlimited power (and responsibility)
useEffect and useRef in ReactBefore shipping any visualization, verify:
Read references/tufte-principles.md for deep dive.
The New York Times graphics team process:
Read references/nyt-workflow.md for case studies.
Data viz isn't static. Movement communicates:
// Use spring physics, not linear easing
const springConfig = {
type: "spring",
stiffness: 300,
damping: 30
};
// Stagger for multiple elements
const staggerChildren = {
delayChildren: 0.1,
staggerChildren: 0.05
};
// Respect prefers-reduced-motion
const shouldAnimate = !window.matchMedia('(prefers-reduced-motion: reduce)').matches;
Read references/animation-patterns.md for complete patterns library.
// Qualitative (categorical data)
const categorical = [
"#d97706", "#7c3aed", "#059669", "#dc2626", "#2563eb"
];
// Sequential (ordered data, low to high)
const sequential = [
"#fef3c7", "#fcd34d", "#f59e0b", "#d97706", "#92400e"
];
// Diverging (data with meaningful center)
const diverging = [
"#dc2626", "#f87171", "#fef2f2", "#c7d2fe", "#6366f1"
];
# Percy - Automated visual testing
npx percy snapshot ./storybook-static
# Chromatic - For Storybook
npx chromatic --project-token=<token>
// Verify rendered elements match data
test('bar chart renders correct number of bars', () => {
const data = [{ x: 'A', y: 10 }, { x: 'B', y: 20 }];
render(<BarChart data={data} />);
const bars = screen.getAllByTestId('bar');
expect(bars).toHaveLength(2);
});
// Verify scale accuracy
test('bar heights proportional to values', () => {
const data = [{ x: 'A', y: 10 }, { x: 'B', y: 20 }];
render(<BarChart data={data} />);
const bars = screen.getAllByTestId('bar');
const heights = bars.map(b => parseInt(b.style.height));
expect(heights[1]).toBe(heights[0] * 2); // B is 2x A
});
Read references/testing-strategies.md for comprehensive test suites.
// Desktop: Show everything
// Tablet: Simplify axes, reduce labels
// Mobile: Minimal chart, key insights only
const ChartResponsive = ({ data }: Props) => {
const isMobile = useMediaQuery('(max-width: 640px)');
return (
<ResponsiveContainer width="100%" height={isMobile ? 200 : 400}>
<LineChart data={data}>
{!isMobile && <CartesianGrid strokeDasharray="3 3" />}
<XAxis
dataKey="date"
tick={isMobile ? { fontSize: 10 } : undefined}
interval={isMobile ? 'preserveStartEnd' : 'auto'}
/>
<YAxis tick={isMobile ? false : undefined} />
<Tooltip />
<Line type="monotone" dataKey="value" stroke="#d97706" />
</LineChart>
</ResponsiveContainer>
);
};
Every visualization tells a story. Follow the narrative arc:
Read references/data-storytelling.md for narrative frameworks.
Problem: 12 colors, tiny slices, legend on the side
Solution: Max 5 categories, direct labels, consider bar chart instead
Problem: Y-axis doesn't start at zero, exaggerates differences
Solution: Always start at zero for bar charts (lines can vary)
Problem: Two Y-axes with different scales mislead viewers
Solution: Use separate charts or normalize to same scale
Problem: 3D effects distort data perception
Solution: Stick to 2D, use color/size for third dimension
Problem: Empty screen with spinner for 2+ seconds
Solution: Skeleton loading that shows chart structure immediately
Read references/antipatterns.md for exhaustive catalog.
# Use Observable Plot for rapid iteration
npm install @observablehq/plot
# Create throwaway prototypes, iterate fast
# When you find the right chart, implement in production library
// Use Recharts for standard charts
// Use Nivo for beautiful, themeable charts
// Use Visx/D3 for custom visualizations
// Always wrap in error boundaries
// Always show skeleton loading state
// Always handle empty/loading/error states
# Visual regression testing
npx percy snapshot
# Component testing
npm test -- --coverage
# Accessibility testing
npx axe-core src/components/charts
// Storybook for component playground
// Props documentation with TypeScript
// Usage examples for each chart type
const generateInsight = async (data: DataPoint[]) => {
const response = await fetch('/api/claude', {
method: 'POST',
body: JSON.stringify({
model: 'claude-haiku',
prompt: `Analyze this data and provide ONE key insight (max 15 words): ${JSON.stringify(data)}`
})
});
return response.text(); // "Sales peaked in Q3, driven by mobile conversions"
};
Study these regularly:
// ❌ DON'T import entire library
import { LineChart } from 'recharts';
// ✅ DO tree-shake where possible
import LineChart from 'recharts/lib/chart/LineChart';
// Use dynamic imports for heavy charts
const HeavyChart = dynamic(() => import('./HeavyChart'), {
loading: () => <ChartSkeleton />,
ssr: false // Disable SSR for client-only charts
});
For large datasets, render only visible portion:
// Use react-window or react-virtualized for long lists
// Aggregate/sample data for chart display
// Store full dataset separately for export
prefers-reduced-motion: reduce<figure role="img" aria-labelledby="chart-title chart-desc">
<h2 id="chart-title">Sales Over Time</h2>
<p id="chart-desc">
Line chart showing sales increased 45% from Q1 to Q4,
peaking in November at $2.3M.
</p>
<LineChart data={data} />
{/* Provide data table alternative */}
<details>
<summary>View data table</summary>
<table>...</table>
</details>
</figure>
This skill includes comprehensive reference documentation:
references/tufte-principles.md - Edward Tufte's data visualization principles with examplesreferences/library-comparison.md - Deep dive on Observable Plot, Recharts, Nivo, Visx, D3references/testing-strategies.md - Visual regression, component testing, accessibility testingreferences/animation-patterns.md - Motion design patterns for chartsreferences/data-storytelling.md - Narrative techniques and scrollytelling patternsreferences/antipatterns.md - Common mistakes and how to avoid themreferences/nyt-workflow.md - New York Times graphics team best practicesscripts/data-transform.ts - Common data transformations (rollup, pivot, normalize)scripts/chart-test-helpers.ts - Testing utilities for verifying chart accuracyscripts/color-palette-generator.ts - Generate accessible color palettesscripts/performance-benchmark.ts - Benchmark chart rendering performance// 1. Install dependencies
// npm install recharts framer-motion
// 2. Create a simple line chart
import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';
import { motion } from 'framer-motion';
const data = [
{ month: 'Jan', value: 400 },
{ month: 'Feb', value: 300 },
{ month: 'Mar', value: 600 },
];
export const SalesChart = () => (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<ResponsiveContainer width="100%" height={300}>
<LineChart data={data}>
<XAxis dataKey="month" />
<YAxis />
<Tooltip />
<Line
type="monotone"
dataKey="value"
stroke="#d97706"
strokeWidth={2}
dot={{ fill: '#d97706', r: 4 }}
/>
</LineChart>
</ResponsiveContainer>
</motion.div>
);
// 3. Test it
// 4. Ship it with confidence
Remember: The best visualization is the one that makes the insight obvious. When in doubt, simplify. When confused, prototype 10 options. When shipping, test ruthlessly.
This skill guides: Chart selection | Library integration | Testing strategies | Animation patterns | Accessibility compliance | Performance optimization
Statistical visualization with pandas integration. Use for quick exploration of distributions, relationships, and categorical comparisons with attractive defaults. Best for box plots, violin plots, pair plots, heatmaps. Built on matplotlib. For interactive plots use plotly; for publication styling use scientific-visualization.
Populates investment banking pitch deck templates with data from source files. Use when: user provides a PowerPoint template to fill in, user has source data (Excel/CSV) to populate into slides, user mentions populating or filling a pitch deck template, or user needs to transfer data into existing slide layouts. Not for creating presentations from scratch.
Statistical visualization with pandas integration. Use for quick exploration of distributions, relationships, and categorical comparisons with attractive defaults. Best for box plots, violin plots, pair plots, heatmaps. Built on matplotlib. For interactive plots use plotly; for publication styling use scientific-visualization.
Meta-skill for publication-ready figures. Use when creating journal submission figures requiring multi-panel layouts, significance annotations, error bars, colorblind-safe palettes, and specific journal formatting (Nature, Science, Cell). Orchestrates matplotlib/seaborn/plotly with publication styles. For quick exploration use seaborn or plotly directly.
Statistical visualization with pandas integration. Use for quick exploration of distributions, relationships, and categorical comparisons with attractive defaults. Best for box plots, violin plots, pair plots, heatmaps. Built on matplotlib. For interactive plots use plotly; for publication styling use scientific-visualization.
Transform text-heavy slides into visual storytelling. Suggest layout improvements, icon usage, and data visualization.
Aggregate and rank signals from multiple edge-finding skills (edge-candidate-agent, theme-detector, sector-analyst, institutional-flow-tracker) into a prioritized conviction dashboard with weighted scoring, deduplication, and contradiction detection.
Optimize Core Web Vitals (LCP, INP, CLS) for better page experience and search ranking. Use when asked to "improve Core Web Vitals", "fix LCP", "reduce CLS", "optimize INP", "page experience optimization", or "fix layout shifts". Focuses specifically on the three Core Web Vitals metrics. Do NOT use for general web performance (use perf-web-optimization), Lighthouse audits (use perf-lighthouse), or Astro-specific optimization (use perf-astro).
Take curiositech/data-viz-2025 from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference npm, npx.
Without those the skill loads but fails at the first command.