feat(08-01): create ChartCard wrapper and ChartTooltip components

- ChartCard: shared chart card with title, period badge, empty state, role="img" accessibility
- ChartTooltip: custom Recharts tooltip with design system tokens, color dots, mono font
- Both use CSS custom properties for automatic dark mode support
- TypeScript compiles cleanly with Recharts TooltipContentProps type

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 4.6 committed 2026-03-27 08:47:07 -04:00
1 parent a45e6bc10c
commit 31328d5a3b
2 files changed
+111

No files matched your search

@@ -0,0 +1,57 @@
import type { TooltipContentProps } from "recharts";
interface ChartTooltipExtraProps {
valuePrefix?: string;
valueSuffix?: string;
}
type Props = Partial<TooltipContentProps<number, string>> & ChartTooltipExtraProps;
export function ChartTooltip({
active,
payload,
label,
valuePrefix = "",
valueSuffix = "",
}: Props) {
if (!active || !payload?.length) return null;
const dateStr =
typeof label === "string"
? new Date(label + "T00:00:00").toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
})
: String(label);
return (
<div
className="rounded-[6px] border border-[color:var(--line-strong)] bg-[color:var(--panel)] px-3 py-2 shadow-md"
style={{ maxWidth: 200, zIndex: 50 }}
>
<div className="text-[11px] text-[color:var(--muted)]">{dateStr}</div>
{payload.map((entry, i) => (
<div key={i} className="mt-1 flex items-center gap-2">
<span
className="inline-block h-2 w-2 shrink-0 rounded-full"
style={{ backgroundColor: entry.color }}
/>
<span
className="text-[13px] font-semibold text-[color:var(--text)]"
style={{
fontFamily: "var(--font-mono)",
fontVariantNumeric: "tabular-nums",
}}
>
{valuePrefix}
{typeof entry.value === "number"
? entry.value.toLocaleString()
: entry.value}
{valueSuffix}
</span>
</div>
))}
</div>
);
}