Analytics
ChartContainer
The shell every Crew OS chart sits in: it wraps a Recharts 2 chart in a ResponsiveContainer, turns a config object into per-series colour variables and styles axes, grid, tooltip and legend with the theme tokens.
Guidelines#
Use it for analytics cards: activity over time (area, line), volume by stage or channel (bar), shares (pie, radial).
What you provide: config (required), a map from each dataKey to { label, icon?, color } or { label, theme: { light, dark } }; each entry becomes --color-<key>, so series use stroke="var(--color-sent)". theme colours switch on a .dark ancestor. Build the chart from Recharts primitives (in this bundle: DC.Recharts.AreaChart, Area, BarChart, Bar, LineChart, XAxis, CartesianGrid, …). Add ChartTooltip with content={<ChartTooltipContent />} (indicator: dot default, line, dashed; hideLabel, hideIndicator, nameKey, labelKey, formatter) and ChartLegend with ChartLegendContent.
Anatomy: the container defaults to aspect-video and text-xs, so give it a height (h-[240px] w-full). Tick labels are fill-muted-foreground; the default grid stroke becomes border/50; the cursor is border. The tooltip is a bg-background card, rounded-lg, border-border/50, px-2.5 py-1.5, shadow-xl, at least 128px wide, values in font-mono tabular-nums. Legend swatches are 8px squares with 2px corners, 16px apart.
Don't hard-code hex values in series (go through config), or add a second y-axis.
Facts#
| Group | Analytics |
|---|---|
| Product | Crew OS |
| Global | window.DigitalCrew.ChartContainer |
| Source | digitalcrew-orchestrator: components/ui/chart.tsx |
| Import in the app | import { ChartContainer } from "@/components/ui/chart"; |
API#
TypeScript declarations emitted from components/ui/chart.tsx (the module also exports ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent).
declare const THEMES: {
readonly light: "";
readonly dark: ".dark";
};
export type ChartConfig = {
[k in string]: {
label?: React.ReactNode;
icon?: React.ComponentType;
} & ({
color?: string;
theme?: never;
} | {
color?: never;
theme: Record<keyof typeof THEMES, string>;
});
};
declare function ChartContainer({ id, className, children, config, ...props }: React.ComponentProps<"div"> & {
config: ChartConfig;
children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>["children"];
}): any;
declare const ChartStyle: ({ id, config }: {
id: string;
config: ChartConfig;
}) => any;
declare const ChartTooltip: typeof RechartsPrimitive.Tooltip;
declare function ChartTooltipContent({ active, payload, className, indicator, hideLabel, hideIndicator, label, labelFormatter, labelClassName, formatter, color, nameKey, labelKey, }: React.ComponentProps<typeof RechartsPrimitive.Tooltip> & React.ComponentProps<"div"> & {
hideLabel?: boolean;
hideIndicator?: boolean;
indicator?: "line" | "dot" | "dashed";
nameKey?: string;
labelKey?: string;
}): any;
declare const ChartLegend: typeof RechartsPrimitive.Legend;
declare function ChartLegendContent({ className, hideIcon, payload, verticalAlign, nameKey, }: React.ComponentProps<"div"> & Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
hideIcon?: boolean;
nameKey?: string;
}): any;
export { ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, ChartStyle, };Example#
The code of the preview above: plain React.createElement against window.DigitalCrew, with realistic data.
<div id="root" class="p-6"></div>
<script>
(function () {
var h = React.createElement, DC = window.DigitalCrew, I = DC.Icons, R = DC.Recharts;
var data = [
{ day: "Mon", sent: 412, replies: 31 },
{ day: "Tue", sent: 468, replies: 42 },
{ day: "Wed", sent: 455, replies: 38 },
{ day: "Thu", sent: 520, replies: 57 },
{ day: "Fri", sent: 498, replies: 49 },
{ day: "Sat", sent: 120, replies: 9 },
{ day: "Sun", sent: 96, replies: 14 }
];
var config = {
sent: { label: "Emails sent", color: "var(--chart-1)" },
replies: { label: "Replies", color: "var(--chart-2)" }
};
function App() {
return h("div", { className: "grid gap-3" },
h("div", null,
h("p", { className: "text-sm font-medium" }, "Outbound activity"),
h("p", { className: "text-muted-foreground text-xs" }, "Q3 CFO outreach, last 7 days")),
h(DC.ChartContainer, { config: config, className: "h-[240px] w-full" },
h(R.AreaChart, { data: data, margin: { left: 4, right: 12, top: 8 } },
h(R.CartesianGrid, { vertical: false }),
h(R.XAxis, { dataKey: "day", tickLine: false, axisLine: false, tickMargin: 8 }),
h(R.YAxis, { tickLine: false, axisLine: false, width: 32 }),
h(DC.ChartTooltip, { defaultIndex: 3, content: h(DC.ChartTooltipContent, { indicator: "line" }) }),
h(R.Area, { dataKey: "sent", type: "monotone", stroke: "var(--color-sent)", strokeWidth: 2, fill: "var(--color-sent)", fillOpacity: 0.12 }),
h(R.Area, { dataKey: "replies", type: "monotone", stroke: "var(--color-replies)", strokeWidth: 2, fill: "var(--color-replies)", fillOpacity: 0.2 }),
h(DC.ChartLegend, { content: h(DC.ChartLegendContent) }))));
}
ReactDOM.createRoot(document.getElementById("root")).render(h(DC.TooltipProvider, { delayDuration: 80 }, h(App)));
})();
</script>