{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "vertical-tabs",
  "title": "Vertical Tabs",
  "description": "A vertical tab interaction with smooth content switching.",
  "dependencies": [
    "motion",
    "@hugeicons/core-free-icons",
    "@hugeicons/react",
    "clsx",
    "tailwind-merge"
  ],
  "files": [
    {
      "path": "registry/default/example/vertical-tabs.tsx",
      "content": "\"use client\";\n\nimport React, { useState, useEffect, useCallback } from \"react\";\nimport { motion, AnimatePresence } from \"motion/react\";\nimport { cn } from \"@/lib/utils\";\nimport { ArrowLeft01Icon, ArrowRight01Icon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\n\n// Change Here\nconst SERVICES = [\n  {\n    id: \"01\",\n    title: \"Web Design\",\n    description:\n      \"Creating beautiful, functional, and user-centric digital experiences.\",\n    image:\n      \"https://images.unsplash.com/photo-1550745165-9bc0b252726f?q=80&w=1200\",\n  },\n  {\n    id: \"02\",\n    title: \"Framer Development\",\n    description: \"Building high-performance, animated websites with Framer.\",\n    image:\n      \"https://images.unsplash.com/photo-1581291518633-83b4ebd1d83e?q=80&w=1200\",\n  },\n  {\n    id: \"03\",\n    title: \"Branding\",\n    description:\n      \"Defining your brand's visual identity and voice for a lasting impression.\",\n    image:\n      \"https://images.unsplash.com/photo-1555066931-4365d14bab8c?q=80&w=1200\",\n  },\n];\n\nconst AUTO_PLAY_DURATION = 5000;\n\nexport default function VerticalTabs() {\n  const [activeIndex, setActiveIndex] = useState(0);\n  const [direction, setDirection] = useState(0);\n  const [isPaused, setIsPaused] = useState(false);\n\n  const handleNext = useCallback(() => {\n    setDirection(1);\n    setActiveIndex((prev) => (prev + 1) % SERVICES.length);\n  }, []);\n\n  const handlePrev = useCallback(() => {\n    setDirection(-1);\n    setActiveIndex((prev) => (prev - 1 + SERVICES.length) % SERVICES.length);\n  }, []);\n\n  const handleTabClick = (index: number) => {\n    if (index === activeIndex) return;\n    setDirection(index > activeIndex ? 1 : -1);\n    setActiveIndex(index);\n    setIsPaused(false);\n  };\n\n  useEffect(() => {\n    if (isPaused) return;\n\n    const interval = setInterval(() => {\n      handleNext();\n    }, AUTO_PLAY_DURATION);\n\n    return () => clearInterval(interval);\n  }, [activeIndex, isPaused, handleNext]);\n\n  const variants = {\n    enter: (direction: number) => ({\n      y: direction > 0 ? \"-100%\" : \"100%\",\n      opacity: 0,\n    }),\n    center: {\n      zIndex: 1,\n      y: 0,\n      opacity: 1,\n    },\n    exit: (direction: number) => ({\n      zIndex: 0,\n      y: direction > 0 ? \"100%\" : \"-100%\",\n      opacity: 0,\n    }),\n  };\n\n  return (\n    <section className=\"w-full bg-background py-8 md:py-16 lg:py-24\">\n      <div className=\"w-full px-4 md:px-8 lg:px-12 xl:px-20 mx-auto\">\n        <div className=\"grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-16 items-start\">\n          {/* Left Column: Content */}\n          <div className=\"lg:col-span-5 flex flex-col justify-center order-2 lg:order-1 pt-4\">\n            <div className=\"space-y-1 mb-12\">\n              <h2 className=\"tracking-tighter text-balance text-3xl font-medium md:text-4xl lg:text-5xl text-foreground\">\n                How I can help you\n              </h2>\n              <span className=\"text-[10px] font-medium text-muted-foreground uppercase tracking-[0.3em] block ml-0.5\">\n                (SERVICES)\n              </span>\n            </div>\n\n            <div className=\"flex flex-col space-y-0\">\n              {SERVICES.map((service, index) => {\n                const isActive = activeIndex === index;\n                return (\n                  <button\n                    key={service.id}\n                    onClick={() => handleTabClick(index)}\n                    className={cn(\n                      \"group relative flex items-start gap-4 py-6 md:py-8 text-left transition-all duration-500 border-t border-border/50 first:border-0\",\n                      isActive\n                        ? \"text-foreground\"\n                        : \"text-muted-foreground/60 hover:text-foreground\"\n                    )}\n                  >\n                    <div className=\"absolute left-[-16px] md:left-[-24px] top-0 bottom-0 w-[2px] bg-muted\">\n                      {isActive && (\n                        <motion.div\n                          key={`progress-${index}-${isPaused}`}\n                          className=\"absolute top-0 left-0 w-full bg-foreground origin-top\"\n                          initial={{ height: \"0%\" }}\n                          animate={\n                            isPaused ? { height: \"0%\" } : { height: \"100%\" }\n                          }\n                          transition={{\n                            duration: AUTO_PLAY_DURATION / 1000,\n                            ease: \"linear\",\n                          }}\n                        />\n                      )}\n                    </div>\n\n                    <span className=\"text-[9px] md:text-[10px] font-medium mt-1 tabular-nums opacity-50\">\n                      /{service.id}\n                    </span>\n\n                    <div className=\"flex flex-col gap-2 flex-1\">\n                      <span\n                        className={cn(\n                          \"text-2xl md:text-3xl lg:text-4xl font-normal tracking-tight transition-colors duration-500\",\n                          isActive ? \"text-foreground\" : \"\"\n                        )}\n                      >\n                        {service.title}\n                      </span>\n\n                      <AnimatePresence mode=\"wait\">\n                        {isActive && (\n                          <motion.div\n                            initial={{ opacity: 0, height: 0 }}\n                            animate={{ opacity: 1, height: \"auto\" }}\n                            exit={{ opacity: 0, height: 0 }}\n                            transition={{\n                              duration: 0.3,\n                              ease: [0.23, 1, 0.32, 1],\n                            }}\n                            className=\"overflow-hidden\"\n                          >\n                            <p className=\"text-muted-foreground text-sm md:text-base font-normal leading-relaxed max-w-sm pb-2\">\n                              {service.description}\n                            </p>\n                          </motion.div>\n                        )}\n                      </AnimatePresence>\n                    </div>\n                  </button>\n                );\n              })}\n            </div>\n          </div>\n\n          <div className=\"lg:col-span-7 flex flex-col justify-end h-full order-1 lg:order-2\">\n            <div\n              className=\"relative group/gallery\"\n              onMouseEnter={() => setIsPaused(true)}\n              onMouseLeave={() => setIsPaused(false)}\n            >\n              <div className=\"relative aspect-4/5 md:aspect-4/3 lg:aspect-16/11 rounded-3xl md:rounded-[2.5rem] overflow-hidden bg-muted/30 border border-border/40\">\n                <AnimatePresence\n                  initial={false}\n                  custom={direction}\n                  mode=\"popLayout\"\n                >\n                  <motion.div\n                    key={activeIndex}\n                    custom={direction}\n                    variants={variants}\n                    initial=\"enter\"\n                    animate=\"center\"\n                    exit=\"exit\"\n                    transition={{\n                      y: { type: \"spring\", stiffness: 260, damping: 32 },\n                      opacity: { duration: 0.4 },\n                    }}\n                    className=\"absolute inset-0 w-full h-full cursor-pointer\"\n                    onClick={handleNext}\n                  >\n                    <img\n                      src={SERVICES[activeIndex].image}\n                      alt={SERVICES[activeIndex].title}\n                      className=\"w-full h-full object-cover transition-transform duration-700 hover:scale-105 m-0! p-0! block\"\n                    />\n\n                    <div className=\"absolute inset-x-0 bottom-0 h-1/3 bg-linear-to-t from-black/20 via-transparent to-transparent opacity-60\" />\n                  </motion.div>\n                </AnimatePresence>\n\n                <div className=\"absolute bottom-6 right-6 md:bottom-8 md:right-8 flex gap-2 md:gap-3 z-20\">\n                  <button\n                    onClick={(e) => {\n                      e.stopPropagation();\n                      handlePrev();\n                    }}\n                    className=\"w-10 h-10 md:w-12 md:h-12 rounded-full bg-background/80 backdrop-blur-md border border-border/50 flex items-center justify-center text-foreground hover:bg-background transition-all active:scale-90\"\n                    aria-label=\"Previous\"\n                  >\n                    <HugeiconsIcon icon={ArrowLeft01Icon} size={20} />\n                  </button>\n                  <button\n                    onClick={(e) => {\n                      e.stopPropagation();\n                      handleNext();\n                    }}\n                    className=\"w-10 h-10 md:w-12 md:h-12 rounded-full bg-background/80 backdrop-blur-md border border-border/50 flex items-center justify-center text-foreground hover:bg-background transition-all active:scale-90\"\n                    aria-label=\"Next\"\n                  >\n                    <HugeiconsIcon icon={ArrowRight01Icon} size={20} />\n                  </button>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </section>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}