All componentsdata
Charts
Recharts wrappers themed with --chart-1 through --chart-5 instead of the library defaults.
Line
Bar
Area
Pie
Installation
$ npx shadcn@latest add https://deepmoon.dev/r/charts.jsonnpm dependencies: recharts
Props
ChartProps
| Prop | Type | Required |
|---|---|---|
| data | Record<string, string | number>[] | Yes |
| index | string | Yes |
| series | ChartSeries[] | Yes |
| height | number | No |
| className | string | No |
PieChartProps
| Prop | Type | Required |
|---|---|---|
| data | PieChartDatum[] | Yes |
| height | number | No |
| className | string | No |
Code
charts.tsx
"use client";
import * as React from "react";
import type { TooltipContentProps } from "recharts";
import {
ResponsiveContainer,
LineChart as RLineChart,
Line,
BarChart as RBarChart,
Bar,
AreaChart as RAreaChart,
Area,
PieChart as RPieChart,
Pie,
Cell,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
} from "recharts";
import { cn } from "@/lib/utils";
/** The deepmoon accent ramp — every chart cycles through these instead of a hardcoded palette. */
const CHART_COLORS = ["var(--chart-1)", "var(--chart-2)", "var(--chart-3)", "var(--chart-4)", "var(--chart-5)"];
function ChartTooltip({ active, payload, label }: TooltipContentProps) {
if (!active || !payload?.length) return null;
return (
<div className="rounded-md border border-border bg-popover px-3 py-2 text-xs text-popover-foreground shadow-md">
{label ? <p className="mb-1 font-medium text-foreground">{label}</p> : null}
<div className="flex flex-col gap-0.5">
{payload.map((entry, index) => (
<div key={index} className="flex items-center gap-1.5">
<span aria-hidden="true" className="size-2 rounded-full" style={{ backgroundColor: entry.color }} />
<span className="text-muted-foreground">{entry.name}:</span>
<span className="font-medium text-foreground">{entry.value}</span>
</div>
))}
</div>
</div>
);
}
const axisProps = {
stroke: "var(--muted-foreground)",
fontSize: 12,
tickLine: false,
axisLine: false,
} as const;
const legendStyle = { fontSize: 12, color: "var(--muted-foreground)" } as const;
export interface ChartSeries {
key: string;
label?: string;
}
export interface ChartProps {
data: Record<string, string | number>[];
index: string;
series: ChartSeries[];
height?: number;
className?: string;
}
export function LineChart({ data, index, series, height = 300, className }: ChartProps) {
return (
<div className={cn("w-full", className)} style={{ height }}>
<ResponsiveContainer width="100%" height="100%">
<RLineChart data={data}>
<CartesianGrid strokeDasharray="3 3" stroke="var(--border)" />
<XAxis dataKey={index} {...axisProps} />
<YAxis {...axisProps} />
<Tooltip content={ChartTooltip} />
<Legend wrapperStyle={legendStyle} />
{series.map((s, i) => (
<Line
key={s.key}
type="monotone"
dataKey={s.key}
name={s.label ?? s.key}
stroke={CHART_COLORS[i % CHART_COLORS.length]}
strokeWidth={2}
dot={false}
/>
))}
</RLineChart>
</ResponsiveContainer>
</div>
);
}
export function BarChart({ data, index, series, height = 300, className }: ChartProps) {
return (
<div className={cn("w-full", className)} style={{ height }}>
<ResponsiveContainer width="100%" height="100%">
<RBarChart data={data}>
<CartesianGrid strokeDasharray="3 3" stroke="var(--border)" />
<XAxis dataKey={index} {...axisProps} />
<YAxis {...axisProps} />
<Tooltip content={ChartTooltip} cursor={{ fill: "var(--accent)" }} />
<Legend wrapperStyle={legendStyle} />
{series.map((s, i) => (
<Bar key={s.key} dataKey={s.key} name={s.label ?? s.key} fill={CHART_COLORS[i % CHART_COLORS.length]} radius={[4, 4, 0, 0]} />
))}
</RBarChart>
</ResponsiveContainer>
</div>
);
}
export function AreaChart({ data, index, series, height = 300, className }: ChartProps) {
return (
<div className={cn("w-full", className)} style={{ height }}>
<ResponsiveContainer width="100%" height="100%">
<RAreaChart data={data}>
<defs>
{series.map((s, i) => (
<linearGradient key={s.key} id={`deepmoon-area-${s.key}`} x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={CHART_COLORS[i % CHART_COLORS.length]} stopOpacity={0.4} />
<stop offset="95%" stopColor={CHART_COLORS[i % CHART_COLORS.length]} stopOpacity={0} />
</linearGradient>
))}
</defs>
<CartesianGrid strokeDasharray="3 3" stroke="var(--border)" />
<XAxis dataKey={index} {...axisProps} />
<YAxis {...axisProps} />
<Tooltip content={ChartTooltip} />
<Legend wrapperStyle={legendStyle} />
{series.map((s, i) => (
<Area
key={s.key}
type="monotone"
dataKey={s.key}
name={s.label ?? s.key}
stroke={CHART_COLORS[i % CHART_COLORS.length]}
fill={`url(#deepmoon-area-${s.key})`}
strokeWidth={2}
/>
))}
</RAreaChart>
</ResponsiveContainer>
</div>
);
}
export interface PieChartDatum {
name: string;
value: number;
}
export interface PieChartProps {
data: PieChartDatum[];
height?: number;
className?: string;
}
export function PieChart({ data, height = 300, className }: PieChartProps) {
return (
<div className={cn("w-full", className)} style={{ height }}>
<ResponsiveContainer width="100%" height="100%">
<RPieChart>
<Tooltip content={ChartTooltip} />
<Legend wrapperStyle={legendStyle} />
<Pie data={data} dataKey="value" nameKey="name" innerRadius="55%" outerRadius="80%" paddingAngle={2}>
{data.map((_, index) => (
<Cell key={index} fill={CHART_COLORS[index % CHART_COLORS.length]} stroke="var(--background)" strokeWidth={2} />
))}
</Pie>
</RPieChart>
</ResponsiveContainer>
</div>
);
}