From data-science
Provides Recharts chart patterns, formatting, and styling for PolicyEngine apps. Covers installation, axis tick configuration, tooltip formatting, and template patterns.
How this skill is triggered — by the user, by Claude, or both
Slash command
/data-science:policyengine-recharts-skillThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use this skill when creating or modifying charts in PolicyEngine applications. PolicyEngine favors **Recharts** over Plotly for frontend charts due to its dramatically smaller bundle size and React-native SVG rendering.
Use this skill when creating or modifying charts in PolicyEngine applications. PolicyEngine favors Recharts over Plotly for frontend charts due to its dramatically smaller bundle size and React-native SVG rendering.
bun install recharts
Do NOT install plotly.js or react-plotly.js for new projects.
import {
LineChart, Line, AreaChart, Area, BarChart, Bar,
ComposedChart, XAxis, YAxis, CartesianGrid, Tooltip,
Legend, ReferenceDot, ReferenceLine, ResponsiveContainer, Label,
} from "recharts";
Recharts' default tick generation produces ugly non-round numbers. Since v3.8.0, Recharts has a built-in niceTicks prop that solves this natively.
Always set niceTicks="snap125" on every <XAxis> and <YAxis>:
<XAxis
dataKey="x"
type="number"
niceTicks="snap125"
domain={["auto", "auto"]}
tickFormatter={tickFormatter}
/>
<YAxis
niceTicks="snap125"
domain={["auto", "auto"]}
tickFormatter={tickFormatter}
/>
The snap125 algorithm snaps tick step sizes to {1, 2, 2.5, 5} × 10^n, producing human-friendly round labels like 0, 5, 10, 15, 20 instead of 0, 4, 8, 12, 16. It may leave some blank space at chart edges — this is the correct trade-off for readability.
Do NOT:
niceTicks() helper function — the built-in prop replaces itniceTicks as a bare boolean or niceTicks="auto" — always specify "snap125" explicitlyniceTicks enum values (always use "snap125"):
| Value | Behavior | Use? |
|---|---|---|
"snap125" | Snaps to {1,2,2.5,5} multiples — roundest labels | Always use this |
"adaptive" | Space-efficient, less round labels | No |
"auto" | Context-dependent, mirrors v2 behavior | No |
"none" | No rounding, raw d3 ticks | No |
Always pair with domain={["auto", "auto"]} — the default domain [0, 'auto'] clamps the minimum to 0, which breaks tick calculation for data that doesn't start at 0 (e.g., all-negative values).
Recharts default tooltip separator is : (with leading space). Always set separator=": " on the Tooltip component.
<Tooltip
contentStyle={TOOLTIP_STYLE}
separator=": "
formatter={(value: number) => [formatCurrency(value), "Label"]}
/>
SVG fill and stroke attributes accept var() directly -- no helper function is needed to resolve CSS custom properties. Use the shadcn/ui chart color variables (--chart-1 through --chart-5) for series colors and standard semantic variables for UI elements:
import {
LineChart, Line, XAxis, YAxis, CartesianGrid,
Tooltip, ResponsiveContainer, Label, ReferenceDot,
} from "recharts";
interface DataPoint { x: number; y: number; }
export default function MyChart({ data, highlightX }: {
data: DataPoint[];
highlightX?: number;
}) {
const fmt = (v: number) => v.toLocaleString("en-US", {
style: "currency", currency: "USD", maximumFractionDigits: 0,
});
const highlightPoint = highlightX != null
? data.reduce((best, d) =>
Math.abs(d.x - highlightX) < Math.abs(best.x - highlightX) ? d : best,
data[0])
: null;
return (
<ResponsiveContainer width="100%" height={350}>
<LineChart data={data} margin={{ left: 20, right: 30, top: 10, bottom: 20 }}>
<CartesianGrid stroke="var(--border)" strokeDasharray="3 3" />
<XAxis
dataKey="x" type="number"
niceTicks="snap125" domain={["auto", "auto"]}
tickFormatter={fmt}
tick={{ fontFamily: "var(--font-sans)", fontSize: 12 }}
>
<Label value="X axis" position="bottom" offset={0} />
</XAxis>
<YAxis
niceTicks="snap125" domain={["auto", "auto"]}
tickFormatter={fmt}
tick={{ fontFamily: "var(--font-sans)", fontSize: 12 }}
>
<Label value="Y axis" angle={-90} position="insideLeft" offset={-5} />
</YAxis>
<Tooltip separator=": " formatter={(v: number) => [fmt(v), "Value"]} />
<Line type="monotone" dataKey="y" stroke="var(--chart-1)" strokeWidth={3} dot={false} />
{highlightPoint && (
<ReferenceDot x={highlightPoint.x} y={highlightPoint.y} r={6}
fill="var(--chart-3)" stroke="var(--chart-3)" />
)}
</LineChart>
</ResponsiveContainer>
);
}
| Use case | Component | Notes |
|---|---|---|
| Single line series | LineChart + Line | Most common |
| Multiple lines | LineChart + multiple Line | Different stroke colors |
| Filled area | AreaChart + Area | Good for cumulative/stacked |
| Stacked areas | ComposedChart + multiple Area | Set fillOpacity={1} |
| Bar chart | BarChart + Bar | Use fill not stroke |
| Mixed line + area | ComposedChart | Combine Line and Area |
Never hardcode hex colors in frontend chart code. Use CSS custom properties directly via var() in SVG attributes:
// Chart series colors (shadcn/ui chart palette)
// Use these for data series — lines, areas, bars, dots
// --chart-1 Primary series (first line/bar)
// --chart-2 Secondary series
// --chart-3 Tertiary series / reference dots
// --chart-4 Fourth series
// --chart-5 Fifth series
// Semantic UI colors — use for chart chrome (grids, borders, backgrounds)
// --border Grid lines, axis lines
// --background Tooltip background
// --foreground Axis labels, tick text
// --primary Interactive UI elements (buttons, links)
// --font-sans Font family
// Usage in JSX — pass var() directly to SVG attributes:
<Line stroke="var(--chart-1)" />
<Area fill="var(--chart-2)" stroke="var(--chart-2)" />
<ReferenceDot fill="var(--chart-3)" stroke="var(--chart-3)" />
<CartesianGrid stroke="var(--border)" />
// Tooltip style object
const TOOLTIP_STYLE = {
background: "var(--background)",
border: "1px solid var(--border)",
borderRadius: 6,
padding: "8px 12px",
};
See policyengine-design-skill for the full token reference.
niceTicks="snap125" on every <XAxis> and <YAxis> — never omit it, never use the bare boolean or "auto"domain={["auto", "auto"]} — required for niceTicks to compute correct domainstype="number" on XAxis when using numeric data keysseparator=": " on TooltipResponsiveContainer with explicit heightdot={false} on Line components for clean curves with many data pointsReferenceDot to highlight the user's current selectionvar(--chart-1) through var(--chart-5) directly to SVG fill/stroke attributes; never hardcode hex values-$31, never $-31Never manually concatenate currency symbols (`$${value}`). Use Intl.NumberFormat with style: 'currency', which handles negative sign placement correctly.
// WRONG — produces "$-31"
const fmt = (v: number) => `$${v.toLocaleString()}`;
// CORRECT — produces "-$31" (Intl handles sign placement)
const fmt = (v: number) => v.toLocaleString("en-US", {
style: "currency", currency: "USD", maximumFractionDigits: 0,
});
In policyengine-app-v2, use formatParameterValue() from @/utils/chartValueUtils or formatCurrency() from @/utils/formatters -- both use Intl.NumberFormat internally.
npx claudepluginhub policyengine/policyengine-claude --plugin analysis-toolsDefines PolicyEngine's visual identity with design tokens, color palettes, typography, and branding guidelines. Provides CSS variables and Tailwind classes for React, Recharts, and Python projects.
Implements MUI X Charts including BarChart, LineChart, PieChart, ScatterChart with custom axes, tooltips, responsive patterns, and composition API in React/TSX/JSX files.
Generates data visualization charts (bar, line, area, pie, doughnut, scatter, radar, treemap) as PNG via Apache ECharts v6 and headless Chromium. Offers 5 style presets, highlight annotations, and retina quality output.