curiositech/data-viz-2025
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
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.