{ "$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) => {\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 \n {/* Image Slider */}\n
\n \n \n \n\n {/* Navigation Buttons */}\n 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 \n \n 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 \n \n\n \n Slide {currentIndex + 1} of {slideCount}\n \n
\n\n {/* Content */}\n \n \n \n \n {slides[currentIndex]?.title}\n \n \n {slides[currentIndex]?.description}\n

\n \n
\n\n {/* Dot Indicators */}\n
\n {slides.map((slide, index) => {\n const isActive = index === currentIndex;\n return (\n 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 \n {slide.title} ({index + 1} of {slideCount})\n \n \n );\n })}\n
\n \n \n );\n}\n", "type": "registry:component", "target": "components/uitripled/image-slider-card-shadcnui.tsx" } ] }