{"$schema":"https://ui.shadcn.com/schema/registry-item.json","author":"Eduardo Calvo <educlopez93@gmail.com>","css":{},"dependencies":["lucide-react","motion"],"description":"Cards stats section block with icons and trends","devDependencies":[],"files":[{"content":"\"use client\";\n\nimport { DollarSign, Smartphone, Star, Users } from \"lucide-react\";\nimport { motion, useInView, useReducedMotion } from \"motion/react\";\nimport React, { useRef } from \"react\";\n\nconst STAGGER_DELAY = 0.1;\nconst ICON_STAGGER_OFFSET = 0.2;\nconst VALUE_DELAY_OFFSET = 0.3;\nconst TREND_STAGGER_OFFSET = 0.5;\n\ninterface StatsCardsProps {\n  description?: string;\n  stats?: Array<{\n    value: string;\n    label: string;\n    description?: string;\n    icon?: string;\n    trend?: {\n      value: string;\n      direction: \"up\" | \"down\";\n    };\n  }>;\n  title?: string;\n}\n\nconst iconMap = {\n  DollarSign,\n  Smartphone,\n  Star,\n  Users,\n};\n\nexport function StatsCards({\n  title = \"Key Metrics\",\n  description = \"Track your success with these important numbers\",\n  stats = [\n    {\n      description: \"Annual recurring revenue\",\n      icon: \"DollarSign\",\n      label: \"Revenue\",\n      trend: { direction: \"up\", value: \"+12%\" },\n      value: \"2.5M\",\n    },\n    {\n      description: \"Happy customers worldwide\",\n      icon: \"Users\",\n      label: \"Customers\",\n      trend: { direction: \"up\", value: \"+8%\" },\n      value: \"45K\",\n    },\n    {\n      description: \"Customer satisfaction rate\",\n      icon: \"Star\",\n      label: \"Satisfaction\",\n      trend: { direction: \"up\", value: \"+2%\" },\n      value: \"98%\",\n    },\n    {\n      description: \"Total app downloads\",\n      icon: \"Smartphone\",\n      label: \"Downloads\",\n      trend: { direction: \"up\", value: \"+15%\" },\n      value: \"1.2M\",\n    },\n  ],\n}: StatsCardsProps) {\n  const ref = useRef(null);\n  const isInView = useInView(ref, { once: true });\n  const shouldReduceMotion = useReducedMotion();\n\n  return (\n    <section className=\"py-20\">\n      <div className=\"mx-auto max-w-7xl px-6\">\n        <motion.div\n          className=\"mb-16 text-center\"\n          initial={shouldReduceMotion ? { opacity: 1 } : { opacity: 0, y: 20 }}\n          transition={shouldReduceMotion ? { duration: 0 } : { duration: 0.6 }}\n          viewport={{ once: true }}\n          whileInView={\n            shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }\n          }\n        >\n          <h2 className=\"mb-4 font-bold text-3xl text-foreground lg:text-4xl\">\n            {title}\n          </h2>\n          <p className=\"mx-auto max-w-2xl text-foreground/70 text-lg\">\n            {description}\n          </p>\n        </motion.div>\n\n        <div\n          className=\"grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-4\"\n          ref={ref}\n        >\n          {stats.map((stat, index) => (\n            <motion.div\n              animate={(() => {\n                if (shouldReduceMotion) {\n                  return { opacity: 1 };\n                }\n                return isInView\n                  ? { opacity: 1, scale: 1, y: 0 }\n                  : { opacity: 0, scale: 0.9, y: 30 };\n              })()}\n              className=\"group relative overflow-hidden rounded-2xl border border-border bg-gradient-to-br from-background to-background/50 p-6 transition-all hover:scale-105 hover:border-brand hover:shadow-xl\"\n              initial={\n                shouldReduceMotion\n                  ? { opacity: 1 }\n                  : { opacity: 0, scale: 0.9, y: 30 }\n              }\n              key={stat.label}\n              transition={\n                shouldReduceMotion\n                  ? { duration: 0 }\n                  : {\n                      delay: index * STAGGER_DELAY,\n                      duration: 0.6,\n                      stiffness: 100,\n                      type: \"spring\" as const,\n                    }\n              }\n            >\n              {/* Icon */}\n              <motion.div\n                animate={(() => {\n                  if (shouldReduceMotion) {\n                    return { rotate: 0, scale: 1 };\n                  }\n                  return isInView\n                    ? { rotate: 0, scale: 1 }\n                    : { rotate: -10, scale: 0.8 };\n                })()}\n                className=\"mb-4 text-3xl\"\n                initial={\n                  shouldReduceMotion\n                    ? { rotate: 0, scale: 1 }\n                    : { rotate: -10, scale: 0.8 }\n                }\n                transition={\n                  shouldReduceMotion\n                    ? { duration: 0 }\n                    : {\n                        delay: index * STAGGER_DELAY + ICON_STAGGER_OFFSET,\n                        duration: 0.6,\n                        stiffness: 200,\n                        type: \"spring\" as const,\n                      }\n                }\n              >\n                {React.createElement(\n                  iconMap[stat.icon as keyof typeof iconMap] || DollarSign,\n                  {\n                    className: \"h-8 w-8\",\n                  }\n                )}\n              </motion.div>\n\n              {/* Value */}\n              <motion.div\n                animate={(() => {\n                  if (shouldReduceMotion) {\n                    return { scale: 1 };\n                  }\n                  return isInView ? { scale: 1 } : { scale: 0.5 };\n                })()}\n                className=\"mb-1 font-bold text-2xl text-foreground lg:text-3xl\"\n                initial={shouldReduceMotion ? { scale: 1 } : { scale: 0.5 }}\n                transition={\n                  shouldReduceMotion\n                    ? { duration: 0 }\n                    : {\n                        delay: index * STAGGER_DELAY + VALUE_DELAY_OFFSET,\n                        duration: 0.8,\n                        stiffness: 200,\n                        type: \"spring\" as const,\n                      }\n                }\n              >\n                {stat.value}\n              </motion.div>\n\n              {/* Label */}\n              <h3 className=\"mb-2 font-semibold text-foreground text-sm uppercase tracking-wide\">\n                {stat.label}\n              </h3>\n\n              {/* Description */}\n              {stat.description ? (\n                <p className=\"mb-3 text-foreground/70 text-xs\">\n                  {stat.description}\n                </p>\n              ) : null}\n\n              {/* Trend */}\n              {stat.trend ? (\n                <motion.div\n                  animate={(() => {\n                    if (shouldReduceMotion) {\n                      return { opacity: 1 };\n                    }\n                    return isInView\n                      ? { opacity: 1, x: 0 }\n                      : { opacity: 0, x: -10 };\n                  })()}\n                  className={`inline-flex items-center rounded-full px-2 py-1 font-medium text-xs ${\n                    stat.trend.direction === \"up\"\n                      ? \"bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-400\"\n                      : \"bg-red-100 text-red-800 dark:bg-red-900/20 dark:text-red-400\"\n                  }`}\n                  initial={\n                    shouldReduceMotion ? { opacity: 1 } : { opacity: 0, x: -10 }\n                  }\n                  transition={\n                    shouldReduceMotion\n                      ? { duration: 0 }\n                      : {\n                          delay: index * STAGGER_DELAY + TREND_STAGGER_OFFSET,\n                          duration: 0.4,\n                        }\n                  }\n                >\n                  <span className=\"mr-1\">\n                    {stat.trend.direction === \"up\" ? \"↗\" : \"↘\"}\n                  </span>\n                  {stat.trend.value}\n                </motion.div>\n              ) : null}\n\n              {/* Hover effect background */}\n              <motion.div\n                className=\"absolute inset-0 bg-gradient-to-br from-brand/10 via-transparent to-transparent opacity-0 group-hover:opacity-100\"\n                initial={{ opacity: 0 }}\n                transition={{ duration: 0.3 }}\n                whileHover={{ opacity: 1 }}\n              />\n            </motion.div>\n          ))}\n        </div>\n      </div>\n    </section>\n  );\n}\n\nexport default StatsCards;\n","path":"index.tsx","target":"components/smoothui/stats-2/index.tsx","type":"registry:block"}],"name":"stats-2","registryDependencies":["https://smoothui.dev/r/tokens.json"],"title":"Stats 2","type":"registry:block"}