一键导入
policyengine-recharts
Recharts chart patterns, formatting, and styling for PolicyEngine apps
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Recharts chart patterns, formatting, and styling for PolicyEngine apps
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
PolicyEngine code writing style guide - formula optimization, direct returns, eliminating unnecessary variables
PolicyEngine parameter patterns - YAML structure, naming conventions, metadata requirements, federal/state separation
PolicyEngine code review patterns - validation checklist, common issues, review standards
PolicyEngine testing patterns - YAML test structure, naming conventions, period handling, and quality standards
PolicyEngine variable patterns - variable creation, no hard-coding principle, federal/state separation, metadata standards
Reference for the /create-dashboard and /deploy-dashboard orchestrated AI workflow
| name | policyengine-recharts |
| description | Recharts chart patterns, formatting, and styling for PolicyEngine apps |
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.