{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "chart", "type": "registry:block", "title": "Chart", "description": "A Recharts-backed chart primitive with Line, Bar, Area, Pie, and Radar variants, a CVD-validated categorical palette, and a shared tooltip/legend.", "author": "Wensity ", "dependencies": [ "clsx", "framer-motion", "recharts", "tailwind-merge" ], "registryDependencies": [], "files": [ { "path": "registry/wensity/lib/utils.ts", "type": "registry:lib", "target": "@lib/utils.ts", "content": "import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n" }, { "path": "registry/wensity/chart.tsx", "type": "registry:component", "target": "@components/wensity/chart.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { useReducedMotion } from \"framer-motion\";\nimport {\n Area,\n AreaChart,\n Bar,\n BarChart,\n CartesianGrid,\n Cell,\n Legend,\n Line,\n LineChart,\n Pie,\n PieChart,\n PolarAngleAxis,\n PolarGrid,\n Radar,\n RadarChart,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from \"recharts\";\nimport { cn } from \"@/lib/utils\";\n\n/* ─── Chart Config & Theming ────────────────────────────────── */\n\nexport type ChartConfig = Record<\n string,\n {\n label: React.ReactNode;\n color?: string;\n }\n>;\n\n/**\n * Six-slot categorical order (E-06), validated for CVD-safe adjacent contrast.\n * Light/dark values live on `--primitive-chart-N`; slot order is the safety\n * mechanism — always assign in this fixed order, never reshuffle per-chart.\n */\nconst CHART_PALETTE = [\n \"var(--primitive-chart-1)\",\n \"var(--primitive-chart-2)\",\n \"var(--primitive-chart-3)\",\n \"var(--primitive-chart-4)\",\n \"var(--primitive-chart-5)\",\n \"var(--primitive-chart-6)\",\n] as const;\n\nconst ChartContext = React.createContext<{ config: ChartConfig } | null>(null);\n\nfunction useChart() {\n const ctx = React.useContext(ChartContext);\n if (!ctx) throw new Error(\"Chart subcomponents must be used inside .\");\n return ctx;\n}\n\n/**\n * Resolves the config/color key for a tooltip or legend entry. For\n * multi-series charts (Line/Bar/Area/Radar) Recharts sets `dataKey` to the\n * series key. For Pie, every slice shares the same `dataKey` (the chart's\n * `dataKey=\"value\"`), so the per-slice identity instead lives in `name` —\n * prefer whichever candidate is an actual config key.\n */\nfunction resolveChartKey(config: ChartConfig, ...candidates: Array) {\n for (const candidate of candidates) {\n if (candidate !== undefined && config[String(candidate)]) return String(candidate);\n }\n return String(candidates.find((c) => c !== undefined) ?? \"\");\n}\n\nexport interface ChartContainerProps extends React.HTMLAttributes {\n config: ChartConfig;\n height?: number;\n children: React.ReactElement;\n}\n\n/**\n * Wraps any Recharts chart with the CVD-validated palette (injected as\n * `--color-` CSS variables pointing at `--primitive-chart-N`) and a\n * responsive frame. Theme switching is handled by the CSS token maps.\n */\nexport const ChartContainer = React.forwardRef(\n ({ config, height = 280, className, children, id, ...props }, ref) => {\n const generatedId = React.useId().replace(/:/g, \"\");\n const chartId = id ?? `chart-${generatedId}`;\n const entries = Object.entries(config);\n\n const colorVars = entries\n .map(([key, value], index) => {\n const color = value.color ?? CHART_PALETTE[index % CHART_PALETTE.length]!;\n return `--color-${key}: ${color};`;\n })\n .join(\" \");\n\n return (\n \n
\n \n \n {children}\n \n
\n
\n );\n }\n);\nChartContainer.displayName = \"ChartContainer\";\n\n/* ─── Tooltip ────────────────────────────────────────────────── */\n\nexport const ChartTooltip = Tooltip;\n\nexport interface ChartTooltipContentProps {\n active?: boolean;\n label?: string | number;\n payload?: Array<{ dataKey?: string | number; name?: string; value?: string | number; color?: string }>;\n labelFormatter?: (label: string | number) => React.ReactNode;\n valueFormatter?: (value: string | number, key: string) => React.ReactNode;\n /** Force every row's label to this config key's label (e.g. a shared \"views\" unit) instead of resolving per-series. */\n nameKey?: string;\n className?: string;\n}\n\nexport function ChartTooltipContent({\n active,\n label,\n payload,\n labelFormatter,\n valueFormatter,\n nameKey,\n className,\n}: ChartTooltipContentProps) {\n const { config } = useChart();\n\n if (!active || !payload?.length) return null;\n\n return (\n \n {label !== undefined && (\n
\n {labelFormatter ? labelFormatter(label) : label}\n
\n )}\n
\n {payload.map((item, index) => {\n const seriesKey = resolveChartKey(config, item.name, item.dataKey);\n const labelEntry = config[nameKey ?? seriesKey];\n return (\n
\n
\n \n {labelEntry?.label ?? item.name ?? seriesKey}\n
\n \n {item.value !== undefined\n ? valueFormatter?.(item.value, seriesKey) ?? item.value\n : null}\n \n
\n );\n })}\n
\n \n );\n}\n\n/* ─── Legend ─────────────────────────────────────────────────── */\n\nexport const ChartLegend = Legend;\n\nexport interface ChartLegendContentProps {\n payload?: Array<{ dataKey?: string | number; value?: string; color?: string }>;\n className?: string;\n}\n\nexport function ChartLegendContent({ payload, className }: ChartLegendContentProps) {\n const { config } = useChart();\n\n if (!payload?.length) return null;\n\n return (\n
\n {payload.map((item, index) => {\n const key = resolveChartKey(config, item.value, item.dataKey);\n const entry = config[key];\n return (\n
\n \n {entry?.label ?? item.value ?? key}\n
\n );\n })}\n
\n );\n}\n\n/* ─── Shared axis chrome ─────────────────────────────────────── */\n\nconst axisTick = {\n fill: \"var(--muted-foreground)\",\n fontSize: \"var(--primitive-text-hint)\",\n fontVariantNumeric: \"tabular-nums\" as const,\n};\nconst gridStroke = \"var(--border)\";\n\nfunction useChartMotion() {\n const reduced = useReducedMotion();\n return {\n isAnimationActive: !reduced,\n animationDuration: reduced ? 0 : 700,\n animationEasing: \"ease-out\" as const,\n };\n}\n\n/* ─── Ready-made chart variants ─────────────────────────────── */\n\nexport interface ChartSeriesDatum {\n [key: string]: string | number;\n}\n\nexport interface ChartVariantProps {\n data: ChartSeriesDatum[];\n config: ChartConfig;\n xKey: string;\n className?: string;\n height?: number;\n showLegend?: boolean;\n showGrid?: boolean;\n stacked?: boolean;\n}\n\n/** Line — displays trends and changes over time. */\nexport function ChartLine({\n data,\n config,\n xKey,\n className,\n height,\n showLegend = true,\n showGrid = true,\n}: ChartVariantProps) {\n const motionProps = useChartMotion();\n const seriesKeys = Object.keys(config);\n\n return (\n \n \n {showGrid && }\n \n \n } />\n {showLegend && seriesKeys.length > 1 && } />}\n {seriesKeys.map((key) => (\n \n ))}\n \n \n );\n}\n\n/** Bar — compares values across different categories. */\nexport function ChartBar({\n data,\n config,\n xKey,\n className,\n height,\n showLegend = true,\n showGrid = true,\n stacked = false,\n}: ChartVariantProps) {\n const motionProps = useChartMotion();\n const seriesKeys = Object.keys(config);\n\n return (\n \n \n {showGrid && }\n \n \n } />\n {showLegend && seriesKeys.length > 1 && } />}\n {seriesKeys.map((key, index) => (\n \n ))}\n \n \n );\n}\n\n/** Area — shows trends with emphasized data volume. */\nexport function ChartArea({\n data,\n config,\n xKey,\n className,\n height,\n showLegend = true,\n showGrid = true,\n stacked = false,\n}: ChartVariantProps) {\n const motionProps = useChartMotion();\n const seriesKeys = Object.keys(config);\n\n return (\n \n \n {showGrid && }\n \n \n } />\n {showLegend && seriesKeys.length > 1 && } />}\n {seriesKeys.map((key) => (\n \n ))}\n \n \n );\n}\n\n/** Pie — displays proportional parts of a whole. */\nexport interface ChartPieDatum {\n name: string;\n value: number;\n}\n\nexport interface ChartPieProps {\n data: ChartPieDatum[];\n config: ChartConfig;\n className?: string;\n height?: number;\n /** Hole size — number (px) or percentage string (e.g. `\"68%\"`). Prefer `%` for responsive donuts. */\n innerRadius?: number | string;\n outerRadius?: number | string;\n showLegend?: boolean;\n /**\n * Content centered on the donut hole. Positioned against the chart frame so it\n * stays aligned with the ring (do not overlay from outside ChartPie).\n */\n centerLabel?: React.ReactNode;\n /**\n * Gap between slices in degrees. Defaults to `0` for a continuous ring.\n * Pass a small value (e.g. `2`) for separated wedges.\n */\n paddingAngle?: number;\n /**\n * Slice outline width. Defaults to `0` so adjacent fills meet cleanly.\n * Pair with `stroke` when you want separated segments.\n */\n strokeWidth?: number;\n /** Slice outline color. Only visible when `strokeWidth` > 0. */\n stroke?: string;\n}\n\nexport function ChartPie({\n data,\n config,\n className,\n height = 280,\n innerRadius = 0,\n outerRadius,\n showLegend = true,\n centerLabel,\n paddingAngle = 0,\n strokeWidth = 0,\n stroke = \"var(--background)\",\n}: ChartPieProps) {\n const motionProps = useChartMotion();\n // Symmetric margins keep the ring’s visual center on the frame mid-point so\n // `centerLabel` (and external overlays on the same box) stay aligned.\n const margin = showLegend\n ? { top: 8, right: 8, left: 8, bottom: 8 }\n : { top: 0, right: 0, left: 0, bottom: 0 };\n const resolvedOuterRadius = outerRadius ?? (showLegend ? \"75%\" : \"90%\");\n\n return (\n \n \n \n } />\n {showLegend ? } /> : null}\n \n {data.map((entry) => (\n \n ))}\n \n \n \n {centerLabel ? (\n \n {centerLabel}\n \n ) : null}\n \n );\n}\n\n/** Radar — compares multiple metrics across categories. */\nexport function ChartRadar({\n data,\n config,\n xKey,\n className,\n height,\n showLegend = true,\n}: Omit) {\n const motionProps = useChartMotion();\n const seriesKeys = Object.keys(config);\n\n return (\n \n \n \n \n } />\n {showLegend && seriesKeys.length > 1 && } />}\n {seriesKeys.map((key) => (\n \n ))}\n \n \n );\n}\n\n/* ─── Interactive bar chart (toggleable series + running totals) ────── */\n\nexport interface ChartBarInteractiveProps {\n data: ChartSeriesDatum[];\n config: ChartConfig;\n xKey: string;\n /** Toggleable series keys, in display order. Defaults to every key in `config`. */\n series?: string[];\n title: React.ReactNode;\n description?: React.ReactNode;\n /** Overrides the tooltip row label for every series (e.g. a shared unit like \"Page Views\"). */\n unitLabel?: React.ReactNode;\n className?: string;\n height?: number;\n /** Format the xKey axis/tooltip label as a date (expects ISO date strings). */\n dateFormat?: boolean;\n /**\n * Force exactly this many data points between shown x-axis ticks (e.g. 4 for\n * daily data = a tick every 4 days). Omit to let ticks auto-space to fit —\n * the safe default for larger datasets, since a fixed gap can overlap when\n * there are many points.\n */\n tickGapDays?: number;\n}\n\nconst shortDateFormatter = new Intl.DateTimeFormat(\"en-US\", { month: \"short\", day: \"numeric\" });\nconst longDateFormatter = new Intl.DateTimeFormat(\"en-US\", { month: \"short\", day: \"numeric\", year: \"numeric\" });\n\nexport function ChartBarInteractive({\n data,\n config,\n xKey,\n series,\n title,\n description,\n unitLabel,\n className,\n height = 250,\n dateFormat = false,\n tickGapDays,\n}: ChartBarInteractiveProps) {\n const motionProps = useChartMotion();\n const seriesKeys = series ?? Object.keys(config);\n const [active, setActive] = React.useState(seriesKeys[0]);\n\n const totals = React.useMemo(() => {\n const sums: Record = {};\n for (const key of seriesKeys) {\n sums[key] = data.reduce((sum, row) => sum + (Number(row[key]) || 0), 0);\n }\n return sums;\n }, [data, seriesKeys]);\n\n const chartConfig = React.useMemo(\n () => (unitLabel ? { ...config, __unit: { label: unitLabel } } : config),\n [config, unitLabel]\n );\n\n return (\n \n
\n
\n \n {title}\n \n {description && (\n {description}\n )}\n
\n
\n {seriesKeys.map((key) => (\n setActive(key)}\n className={cn(\n \"relative flex flex-1 flex-col justify-center gap-1 border-t border-[var(--border)] px-6 py-4 text-left transition-colors duration-150 ease-out\",\n \"even:border-l\",\n \"active:scale-[0.98] motion-reduce:active:scale-100\",\n \"sm:border-t-0 sm:border-l sm:px-8 sm:py-6\",\n active === key && \"bg-[var(--primitive-surface-selected)]\"\n )}\n >\n \n {config[key]?.label ?? key}\n \n \n {(totals[key] ?? 0).toLocaleString()}\n \n \n ))}\n
\n
\n
\n \n \n \n shortDateFormatter.format(new Date(value)) : undefined}\n />\n longDateFormatter.format(new Date(value)) : undefined}\n />\n }\n />\n \n \n \n
\n \n );\n}\n" } ], "cssVars": { "theme": { "font-sans": "var(--font-satoshi, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif)", "font-display": "var(--font-cabinet, ui-sans-serif, system-ui, sans-serif)", "color-background": "var(--background)", "color-foreground": "var(--foreground)", "color-surface": "var(--surface)", "color-surface-muted": "var(--surface-muted)", "color-border": "var(--border)", "color-border-strong": "var(--border-strong)", "color-muted": "var(--muted)", "color-muted-foreground": "var(--muted-foreground)", "color-ring": "var(--ring)", "color-chili-50": "#fff1ee", "color-chili-100": "#ffe2db", "color-chili-200": "#ffa896", "color-chili-300": "#ff8a73", "color-chili-400": "#f15a45", "color-chili-500": "#cd1c18", "color-chili-600": "#b31614", "color-chili-700": "#9b1313", "color-chili-800": "#6a0d0e", "color-chili-900": "#38000a", "color-chili-950": "#1f0006", "color-primitive-surface-elevated": "var(--primitive-surface-elevated)", "color-primitive-surface-overlay": "var(--primitive-surface-overlay)", "color-primitive-surface-hover": "var(--primitive-surface-hover)", "color-primitive-surface-active": "var(--primitive-surface-active)", "color-primitive-surface-selected": "var(--primitive-surface-selected)", "color-primitive-border-subtle": "var(--primitive-border-subtle)", "color-primitive-ring": "var(--primitive-ring)", "color-primitive-text-secondary": "var(--primitive-text-secondary)", "color-primitive-text-placeholder": "var(--primitive-text-placeholder)", "color-primitive-text-inverse": "var(--primitive-text-inverse)", "color-primitive-destructive": "var(--primitive-destructive)", "color-primitive-destructive-hover": "var(--primitive-destructive-hover)", "color-primitive-destructive-active": "var(--primitive-destructive-active)", "color-primitive-destructive-foreground": "var(--primitive-destructive-foreground)", "color-primitive-destructive-surface": "var(--primitive-destructive-surface)", "color-primitive-destructive-border": "var(--primitive-destructive-border)", "color-primitive-success": "var(--primitive-success)", "color-primitive-warning": "var(--primitive-warning)", "color-primitive-info": "var(--primitive-info)", "color-primitive-control-solid": "var(--primitive-control-solid)", "color-primitive-control-solid-hover": "var(--primitive-control-solid-hover)", "color-primitive-control-solid-active": "var(--primitive-control-solid-active)", "color-primitive-control-solid-foreground": "var(--primitive-control-solid-foreground)", "color-primitive-chart-1": "var(--primitive-chart-1)", "color-primitive-chart-2": "var(--primitive-chart-2)", "color-primitive-chart-3": "var(--primitive-chart-3)", "color-primitive-chart-4": "var(--primitive-chart-4)", "color-primitive-chart-5": "var(--primitive-chart-5)", "color-primitive-chart-6": "var(--primitive-chart-6)", "radius-primitive": "var(--primitive-radius)", "radius-primitive-control": "var(--primitive-radius-control)", "radius-primitive-control-sm": "var(--primitive-radius-control-sm)", "radius-primitive-surface": "var(--primitive-radius-surface)", "radius-primitive-item": "var(--primitive-radius-item)", "font-primitive-sans": "var(--primitive-font-sans)", "font-primitive-display": "var(--primitive-font-display)", "font-primitive-mono": "var(--primitive-font-mono)", "spacing-primitive-control-height-sm": "var(--primitive-control-height-sm)", "spacing-primitive-control-height-md": "var(--primitive-control-height-md)", "spacing-primitive-control-height-lg": "var(--primitive-control-height-lg)" }, "light": { "background": "#fafafa", "foreground": "#0a0a0a", "surface": "#ffffff", "surface-muted": "#f4f4f5", "border": "rgba(10, 10, 10, 0.08)", "border-strong": "rgba(10, 10, 10, 0.16)", "muted": "#f4f4f5", "muted-foreground": "#52525b", "ring": "#cd1c18", "pattern-fg": "rgba(10, 10, 10, 0.07)", "llb-primary": "#18181b", "llb-primary-fg": "#ffffff", "llb-success": "#1f883d", "llb-error": "#cf222e", "primitive-surface-elevated": "#ffffff", "primitive-surface-overlay": "#ffffff", "primitive-surface-hover": "color-mix(in srgb, var(--foreground) 4%, transparent)", "primitive-surface-active": "color-mix(in srgb, var(--foreground) 7%, transparent)", "primitive-surface-selected": "color-mix(in srgb, var(--foreground) 6%, transparent)", "primitive-border-subtle": "color-mix(in srgb, var(--border) 60%, transparent)", "primitive-ring": "color-mix(in srgb, var(--foreground) 45%, transparent)", "primitive-text-secondary": "color-mix(in srgb, var(--foreground) 72%, transparent)", "primitive-text-placeholder": "color-mix(in srgb, var(--muted-foreground) 85%, transparent)", "primitive-radius": "0.875rem", "primitive-font-sans": "var(--font-sans)", "primitive-font-display": "var(--font-display)", "primitive-font-mono": "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace", "primitive-control-solid": "#24292d", "primitive-control-solid-hover": "#2c3237", "primitive-control-solid-active": "#1e2326", "primitive-control-solid-foreground": "#ffffff", "primitive-chart-1": "#2a78d6", "primitive-chart-2": "#1baf7a", "primitive-chart-3": "#eda100", "primitive-chart-4": "#008300", "primitive-chart-5": "#4a3aa7", "primitive-chart-6": "#e34948", "primitive-text-inverse": "#ffffff", "primitive-text-hint": "0.6875rem", "primitive-destructive": "#dc2626", "primitive-destructive-hover": "#b91c1c", "primitive-destructive-active": "#991b1b", "primitive-destructive-foreground": "#ffffff", "primitive-destructive-surface": "rgba(220, 38, 38, 0.10)", "primitive-destructive-border": "rgba(220, 38, 38, 0.45)", "primitive-success": "#047857", "primitive-success-surface": "rgba(4, 120, 87, 0.10)", "primitive-success-border": "rgba(4, 120, 87, 0.45)", "primitive-warning": "#b45309", "primitive-warning-surface": "rgba(180, 83, 9, 0.10)", "primitive-warning-border": "rgba(180, 83, 9, 0.45)", "primitive-info": "#0369a1", "primitive-info-surface": "rgba(3, 105, 161, 0.10)", "primitive-info-border": "rgba(3, 105, 161, 0.45)", "primitive-radius-control": "var(--primitive-radius)", "primitive-radius-control-sm": "max(0px, calc(var(--primitive-radius) - 4px))", "primitive-radius-surface": "calc(var(--primitive-radius) + min(2px, var(--primitive-radius)))", "primitive-radius-item": "max(0px, calc(var(--primitive-radius-surface) - 6px))", "primitive-control-height-sm": "2rem", "primitive-control-height-md": "2.25rem", "primitive-control-height-lg": "2.5rem", "primitive-shadow-raised": "0 1px 2px rgba(0, 0, 0, 0.06), 0 8px 24px -12px rgba(0, 0, 0, 0.08)", "primitive-shadow-overlay": "0 1px 2px rgba(0, 0, 0, 0.06), 0 18px 48px -24px rgba(0, 0, 0, 0.35)", "primitive-shadow-modal": "0 1px 2px rgba(0, 0, 0, 0.08), 0 24px 64px -28px rgba(0, 0, 0, 0.42)", "primitive-z-overlay": "100", "primitive-z-popover": "130", "primitive-z-toast": "140", "primitive-backdrop": "rgba(0, 0, 0, 0.6)", "primitive-ease": "cubic-bezier(0.23, 1, 0.32, 1)" }, "dark": { "background": "#0a0a0b", "foreground": "#f5f5f6", "surface": "#111113", "surface-muted": "#18181b", "border": "rgba(255, 255, 255, 0.08)", "border-strong": "rgba(255, 255, 255, 0.14)", "muted": "#1c1c1f", "muted-foreground": "#a1a1aa", "ring": "#cd1c18", "pattern-fg": "rgba(255, 255, 255, 0.06)", "llb-primary": "#f5f5f6", "llb-primary-fg": "#0a0a0b", "llb-success": "#2da44e", "llb-error": "#f85149", "primitive-surface-elevated": "#0f0f10", "primitive-surface-overlay": "#141415", "primitive-surface-hover": "color-mix(in srgb, var(--foreground) 5%, transparent)", "primitive-surface-active": "color-mix(in srgb, var(--foreground) 10%, transparent)", "primitive-surface-selected": "color-mix(in srgb, var(--foreground) 8%, transparent)", "primitive-border-subtle": "color-mix(in srgb, var(--border) 60%, transparent)", "primitive-ring": "color-mix(in srgb, var(--foreground) 45%, transparent)", "primitive-text-secondary": "color-mix(in srgb, var(--foreground) 72%, transparent)", "primitive-text-placeholder": "color-mix(in srgb, var(--muted-foreground) 85%, transparent)", "primitive-radius": "0.875rem", "primitive-font-sans": "var(--font-sans)", "primitive-font-display": "var(--font-display)", "primitive-font-mono": "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace", "primitive-control-solid": "#f5f3ee", "primitive-control-solid-hover": "#ffffff", "primitive-control-solid-active": "#e8e4da", "primitive-control-solid-foreground": "#151719", "primitive-chart-1": "#3987e5", "primitive-chart-2": "#199e70", "primitive-chart-3": "#c98500", "primitive-chart-4": "#008300", "primitive-chart-5": "#9085e9", "primitive-chart-6": "#e66767", "primitive-text-inverse": "#151719", "primitive-text-hint": "0.6875rem", "primitive-destructive": "#e11d2e", "primitive-destructive-hover": "#c1121f", "primitive-destructive-active": "#a4161a", "primitive-destructive-foreground": "#ffffff", "primitive-destructive-surface": "rgba(225, 29, 46, 0.12)", "primitive-destructive-border": "rgba(225, 29, 46, 0.52)", "primitive-success": "#34d399", "primitive-success-surface": "rgba(52, 211, 153, 0.12)", "primitive-success-border": "rgba(52, 211, 153, 0.45)", "primitive-warning": "#fbbf24", "primitive-warning-surface": "rgba(251, 191, 36, 0.12)", "primitive-warning-border": "rgba(251, 191, 36, 0.45)", "primitive-info": "#38bdf8", "primitive-info-surface": "rgba(56, 189, 248, 0.12)", "primitive-info-border": "rgba(56, 189, 248, 0.45)", "primitive-radius-control": "var(--primitive-radius)", "primitive-radius-control-sm": "max(0px, calc(var(--primitive-radius) - 4px))", "primitive-radius-surface": "calc(var(--primitive-radius) + min(2px, var(--primitive-radius)))", "primitive-radius-item": "max(0px, calc(var(--primitive-radius-surface) - 6px))", "primitive-control-height-sm": "2rem", "primitive-control-height-md": "2.25rem", "primitive-control-height-lg": "2.5rem", "primitive-shadow-raised": "0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px -12px rgba(0, 0, 0, 0.6)", "primitive-shadow-overlay": "0 1px 2px rgba(0, 0, 0, 0.4), 0 20px 56px -28px rgba(0, 0, 0, 0.72)", "primitive-shadow-modal": "0 1px 2px rgba(0, 0, 0, 0.45), 0 28px 72px -32px rgba(0, 0, 0, 0.8)", "primitive-z-overlay": "100", "primitive-z-popover": "130", "primitive-z-toast": "140", "primitive-backdrop": "rgba(0, 0, 0, 0.6)", "primitive-ease": "cubic-bezier(0.23, 1, 0.32, 1)" } }, "css": { "@layer base": { ":where([data-wensity-primitive])": { "font-family": "var(--primitive-font-sans)" } }, "@utility scrollbar-hidden": { "scrollbar-width": "none", "-ms-overflow-style": "none", "&::-webkit-scrollbar": { "display": "none" } }, "@keyframes wensity-marquee-x": { "from": { "transform": "translate3d(0, 0, 0)" }, "to": { "transform": "translate3d(-50%, 0, 0)" } }, "@keyframes wensity-marquee-x-reverse": { "from": { "transform": "translate3d(-50%, 0, 0)" }, "to": { "transform": "translate3d(0, 0, 0)" } }, "@keyframes wensity-morph-rot-cw": { "from": { "transform": "rotate(0deg)" }, "to": { "transform": "rotate(360deg)" } }, "@keyframes wensity-morph-rot-ccw": { "from": { "transform": "rotate(0deg)" }, "to": { "transform": "rotate(-360deg)" } } }, "docs": "Free Wensity component. Installs to @components/wensity/chart.tsx. For Pro components and updates, use pnpm dlx wensity@latest add chart.", "categories": [ "UI Primitives", "wensity", "free" ], "meta": { "wensity": { "slug": "chart", "access": "free", "category": "UI Primitives", "kind": "component", "componentUrl": "https://ui.wensity.com/primitives/chart", "shadcnUrl": "https://ui.wensity.com/r/chart", "cliInstall": "pnpm dlx wensity@latest add chart" } } }