{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "testimonial-carousel",
  "type": "registry:component",
  "title": "Testimonial carousel",
  "description": "A React testimonial carousel that auto advances every few seconds with slide transitions, dot indicators using aria-current, and prev and next buttons.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "https://ui.minidev.pro/r/button.json",
    "utils"
  ],
  "files": [
    {
      "path": "registry/premium/testimonial-carousel.tsx",
      "type": "registry:ui",
      "target": "components/ui/testimonial-carousel.tsx",
      "content": "\"use client\"\nimport * as React from \"react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\n\ntype Item = { quote: string; name: string; role: string }\n\nfunction TestimonialCarousel({\n  items = [\n    { quote: \"We replaced four tools with one, and nobody asked to go back.\", name: \"Sam Rivera\", role: \"Founder\" },\n    { quote: \"The first product in years our designers did not want to redesign.\", name: \"Lee Park\", role: \"Design Eng\" },\n    { quote: \"Setup took an afternoon. We shipped the same week.\", name: \"Ava Chen\", role: \"PM\" },\n  ],\n  className,\n}: {\n  items?: Item[]\n  className?: string\n}) {\n  const reduce = useReducedMotion()\n  const [i, setI] = React.useState(0)\n  const item = items[i]\n  React.useEffect(() => {\n    if (reduce || items.length < 2) return\n    const id = window.setInterval(() => setI((x) => (x + 1) % items.length), 4500)\n    return () => window.clearInterval(id)\n  }, [items.length, reduce])\n  return (\n    <div data-slot=\"testimonial-carousel\" data-tier=\"premium\" className={cn(\"rounded-2xl border border-border bg-surface p-5 shadow-raised sm:p-8\", className)}>\n      <AnimatePresence mode=\"wait\">\n        <motion.figure\n          key={item.name + i}\n          initial={reduce ? false : { y: 12 }}\n          animate={{ y: 0 }}\n          exit={reduce ? undefined : { y: -12 }}\n          transition={{ duration: 0.35 }}\n        >\n          <blockquote className=\"text-xl font-medium tracking-[-0.014em] text-fg sm:text-2xl\">“{item.quote}”</blockquote>\n          <figcaption className=\"mt-4 text-sm text-fg-muted\">\n            <span className=\"font-medium text-fg\">{item.name}</span> · {item.role}\n          </figcaption>\n        </motion.figure>\n      </AnimatePresence>\n      <div className=\"mt-6 flex items-center gap-2\">\n        {items.map((_, idx) => (\n          <button\n            key={idx}\n            type=\"button\"\n            aria-label={`Show testimonial ${idx + 1}`}\n            aria-current={idx === i}\n            onClick={() => setI(idx)}\n            className={cn(\"h-1.5 w-6 rounded-full outline-none focus-visible:ring-2 focus-visible:ring-accent\", idx === i ? \"bg-accent\" : \"bg-border\")}\n          />\n        ))}\n        <div className=\"ml-auto flex gap-2\">\n          <Button type=\"button\" size=\"sm\" variant=\"outline\" onClick={() => setI((x) => (x - 1 + items.length) % items.length)}>Prev</Button>\n          <Button type=\"button\" size=\"sm\" variant=\"outline\" onClick={() => setI((x) => (x + 1) % items.length)}>Next</Button>\n        </div>\n      </div>\n    </div>\n  )\n}\nexport { TestimonialCarousel }\n"
    }
  ]
}
