{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "image-slider-card-shadcnui",
  "type": "registry:component",
  "title": "Accessible Image Slider Card",
  "description": "Image carousel card with keyboard support, reduced motion handling, and screen reader-friendly labels",
  "dependencies": [
    "framer-motion",
    "react"
  ],
  "files": [
    {
      "path": "@uitripled/react-shadcn/src/components/components/cards/image-slider-card.tsx",
      "content": "\"use client\";\n\nimport {\n  AnimatePresence,\n  motion,\n  useReducedMotion,\n  type Variants,\n} from \"framer-motion\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { useId, useMemo, useState } from \"react\";\n\ninterface Slide {\n  id: number;\n  image: string;\n  title: string;\n  description: string;\n}\n\nconst slides: Slide[] = [\n  {\n    id: 1,\n    image:\n      \"https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=800&q=80\",\n    title: \"Minimalist Design\",\n    description: \"Clean lines and simple forms create timeless elegance.\",\n  },\n  {\n    id: 2,\n    image:\n      \"https://images.unsplash.com/photo-1618221195710-dd6b41faaea6?w=800&q=80\",\n    title: \"Modern Simplicity\",\n    description: \"Less is more in contemporary visual language.\",\n  },\n  {\n    id: 3,\n    image:\n      \"https://images.unsplash.com/photo-1618005198919-d3d4b5a92ead?w=800&q=80\",\n    title: \"Pure Essence\",\n    description: \"Stripped down to the essential elements.\",\n  },\n];\n\nexport function ImageSliderCard() {\n  const [currentIndex, setCurrentIndex] = useState(0);\n  const shouldReduceMotion = useReducedMotion();\n\n  const headingId = useId();\n  const descriptionId = useId();\n  const tablistId = useId();\n\n  const slideCount = slides.length;\n\n  const imageVariants = useMemo((): Variants => {\n    if (shouldReduceMotion) {\n      return {\n        enter: { opacity: 0 },\n        center: { opacity: 1 },\n        exit: { opacity: 0 },\n      };\n    }\n    return {\n      enter: { opacity: 0, scale: 0.98 },\n      center: { opacity: 1, scale: 1 },\n      exit: { opacity: 0, scale: 1.02 },\n    };\n  }, [shouldReduceMotion]);\n\n  const paginate = (newDirection: number) => {\n    setCurrentIndex((prevIndex) => {\n      const nextIndex = prevIndex + newDirection;\n      if (nextIndex < 0) return slideCount - 1;\n      if (nextIndex >= slideCount) return 0;\n      return nextIndex;\n    });\n  };\n\n  const goToSlide = (index: number) => {\n    if (index === currentIndex) return;\n    setCurrentIndex(index);\n  };\n\n  const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n    switch (event.key) {\n      case \"ArrowLeft\": {\n        event.preventDefault();\n        paginate(-1);\n        break;\n      }\n      case \"ArrowRight\": {\n        event.preventDefault();\n        paginate(1);\n        break;\n      }\n      case \"Home\": {\n        event.preventDefault();\n        goToSlide(0);\n        break;\n      }\n      case \"End\": {\n        event.preventDefault();\n        goToSlide(slideCount - 1);\n        break;\n      }\n      default:\n        break;\n    }\n  };\n\n  return (\n    <motion.div\n      initial={{ opacity: 0, y: 20 }}\n      animate={{ opacity: 1, y: 0 }}\n      transition={{ duration: shouldReduceMotion ? 0 : 0.4 }}\n      className=\"rounded-2xl w-full max-w-3xl bg-card border border-border overflow-hidden focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n      role=\"group\"\n      aria-roledescription=\"carousel\"\n      aria-label=\"Design inspiration image carousel\"\n      aria-live=\"polite\"\n      aria-atomic=\"true\"\n      tabIndex={0}\n      onKeyDown={handleKeyDown}\n    >\n      {/* Image Slider */}\n      <div className=\"relative h-96 bg-muted overflow-hidden\">\n        <AnimatePresence initial={false} mode=\"wait\">\n          <motion.img\n            key={currentIndex}\n            src={slides[currentIndex]?.image}\n            variants={imageVariants}\n            initial=\"enter\"\n            animate=\"center\"\n            exit=\"exit\"\n            transition={{\n              duration: shouldReduceMotion ? 0 : 0.35,\n              ease: \"easeInOut\",\n            }}\n            className=\"absolute w-full h-full object-cover grayscale focus-visible:outline-none transition-opacity\"\n            alt={slides[currentIndex]?.title}\n            role=\"img\"\n            aria-describedby={descriptionId}\n            aria-labelledby={headingId}\n          />\n        </AnimatePresence>\n\n        {/* Navigation Buttons */}\n        <button\n          type=\"button\"\n          onClick={() => paginate(-1)}\n          className=\" absolute left-4 top-1/2 -translate-y-1/2 w-10 h-10 bg-background border border-border flex items-center justify-center hover:bg-muted transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background rounded-full\"\n          aria-label=\"Previous slide\"\n        >\n          <ChevronLeft className=\"w-5 h-5 text-foreground\" aria-hidden=\"true\" />\n        </button>\n        <button\n          type=\"button\"\n          onClick={() => paginate(1)}\n          className=\" absolute right-4 top-1/2 -translate-y-1/2 w-10 h-10 bg-background border border-border flex items-center justify-center hover:bg-muted transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background rounded-full\"\n          aria-label=\"Next slide\"\n        >\n          <ChevronRight\n            className=\"w-5 h-5 text-foreground\"\n            aria-hidden=\"true\"\n          />\n        </button>\n\n        <span className=\"sr-only\" role=\"status\">\n          Slide {currentIndex + 1} of {slideCount}\n        </span>\n      </div>\n\n      {/* Content */}\n      <div\n        className=\"p-8 border-t border-border\"\n        role=\"tablist\"\n        aria-label=\"Slide navigation\"\n        id={tablistId}\n      >\n        <AnimatePresence mode=\"wait\" initial={false}>\n          <motion.div\n            key={currentIndex}\n            initial={{ opacity: 0, y: 10 }}\n            animate={{ opacity: 1, y: 0 }}\n            exit={{ opacity: 0, y: -10 }}\n            transition={{ duration: shouldReduceMotion ? 0 : 0.3 }}\n            role=\"tabpanel\"\n            aria-labelledby={headingId}\n            id={`slide-panel-${slides[currentIndex]?.id ?? \"unknown\"}`}\n          >\n            <h2\n              id={headingId}\n              className=\"text-2xl font-semibold text-foreground mb-2\"\n            >\n              {slides[currentIndex]?.title}\n            </h2>\n            <p\n              id={descriptionId}\n              className=\"text-muted-foreground leading-relaxed\"\n              aria-live=\"polite\"\n            >\n              {slides[currentIndex]?.description}\n            </p>\n          </motion.div>\n        </AnimatePresence>\n\n        {/* Dot Indicators */}\n        <div className=\"flex items-center gap-2 mt-6\">\n          {slides.map((slide, index) => {\n            const isActive = index === currentIndex;\n            return (\n              <button\n                key={slide.id}\n                type=\"button\"\n                onClick={() => goToSlide(index)}\n                className={`rounded-full h-1.5 transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background ${\n                  isActive\n                    ? \"w-8 bg-primary\"\n                    : \"w-1.5 bg-muted hover:bg-muted/80\"\n                }`}\n                aria-label={`Go to slide ${index + 1}`}\n                role=\"tab\"\n                aria-selected={isActive}\n                aria-controls={`slide-panel-${slide.id}`}\n                tabIndex={isActive ? 0 : -1}\n                id={`slide-tab-${slide.id}`}\n              >\n                <span className=\"sr-only\">\n                  {slide.title} ({index + 1} of {slideCount})\n                </span>\n              </button>\n            );\n          })}\n        </div>\n      </div>\n    </motion.div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/uitripled/image-slider-card-shadcnui.tsx"
    }
  ]
}