{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "animated-chart",
  "type": "registry:ui",
  "title": "Animated chart",
  "description": "Compares data ranges and series changes while keeping labels and focus stable.",
  "dependencies": [
    "motion"
  ],
  "categories": [
    "data-commerce"
  ],
  "docs": "https://motion-lexicon.pages.dev/en/components/animated-chart/",
  "meta": {
    "engines": [
      "motion"
    ],
    "runtimeCost": "medium",
    "signature": "Geometry morphs while labels remain readable",
    "sceneFamily": "product-mono",
    "motionRole": "gentle",
    "primaryState": "Reader-series line chart",
    "assetProvenance": "self-contained"
  },
  "files": [
    {
      "path": "src/registry/components/animated-chart.tsx",
      "type": "registry:ui",
      "target": "components/motion-lexicon/animated-chart.tsx",
      "content": "\"use client\";\n\nimport { useId, useState } from \"react\";\nimport { motion, useReducedMotion } from \"motion/react\";\n\nexport type AnimatedChartSeries = { id: string; label: string; values: readonly number[]; color?: string };\nexport type AnimatedChartProps = { series: readonly AnimatedChartSeries[]; labels?: readonly string[]; label?: string; className?: string };\n\nexport function AnimatedChart({ series, labels = [], label = \"Performance chart\", className = \"\" }: AnimatedChartProps) {\n  const reduced = useReducedMotion() === true; const titleId = useId(); const [active, setActive] = useState(0); const selected = series[active]; const max = Math.max(1, ...(selected?.values ?? [1])); const points = (selected?.values ?? []).map((value, index, values) => `${values.length < 2 ? 50 : (index / (values.length - 1)) * 100},${92 - (value / max) * 74}`).join(\" \");\n  return <section aria-labelledby={titleId} className={`rounded-[16px] border border-black/[.1] bg-white p-4 shadow-[0_14px_33px_-26px_rgba(27,27,27,.35)] ${className}`}><div className=\"flex items-start justify-between gap-3\"><div><span className=\"font-mono text-[10px] uppercase tracking-[.16em] text-neutral-500\">Analytics</span><h3 id={titleId} className=\"mt-1 text-[15px] font-medium text-[#292929]\">{label}</h3></div><div role=\"tablist\" aria-label={label} className=\"flex rounded-full bg-neutral-100 p-1\">{series.map((item, index) => <button key={item.id} type=\"button\" role=\"tab\" aria-selected={index === active} onClick={() => setActive(index)} onKeyDown={(event) => { if (event.key === \"ArrowRight\") { event.preventDefault(); setActive((value) => Math.min(series.length - 1, value + 1)); } if (event.key === \"ArrowLeft\") { event.preventDefault(); setActive((value) => Math.max(0, value - 1)); } }} className={`min-h-9 rounded-full px-3 text-[10px] outline-none focus-visible:ring-2 focus-visible:ring-[#4568FF] ${index === active ? \"bg-[#242424] text-white\" : \"text-neutral-500\"}`}>{item.label}</button>)}</div></div><svg role=\"img\" aria-label={`${selected?.label ?? \"\"} ${label}`} viewBox=\"0 0 100 100\" preserveAspectRatio=\"none\" className=\"mt-5 h-40 w-full overflow-visible\"><path d=\"M0 92H100\" stroke=\"#e5e5e5\" strokeWidth=\".7\" /><path d=\"M0 55H100\" stroke=\"#ececec\" strokeWidth=\".7\" strokeDasharray=\"2 3\" /><motion.polyline key={selected?.id} points={points} fill=\"none\" stroke={selected?.color ?? \"#4568FF\"} strokeWidth=\"2.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" initial={reduced ? false : { pathLength: 0, opacity: .4 }} animate={{ pathLength: 1, opacity: 1 }} transition={{ duration: reduced ? 0 : .54, ease: [0.22, 1, 0.36, 1] }} /><circle cx={(selected?.values.length ?? 1) < 2 ? 50 : 100} cy={selected?.values.length ? 92 - (selected.values[selected.values.length - 1] / max) * 74 : 92} r=\"2.8\" fill={selected?.color ?? \"#4568FF\"} /></svg><div className=\"mt-2 flex justify-between font-mono text-[9px] text-neutral-400\">{(labels.length ? labels : selected?.values.map((_, index) => `P${index + 1}`) ?? []).map((item) => <span key={item}>{item}</span>)}</div></section>;\n}\n"
    }
  ]
}
