{ "name": "card-stack", "type": "registry:component", "dependencies": [ "motion" ], "files": [ { "type": "registry:component", "content": "\"use client\";\n\n/**\n * @author: @dorianbaffier\n * @description: Card Stack\n * @version: 1.1.0\n * @date: 2025-06-26\n * @license: MIT\n * @website: https://kokonutui.com\n * @github: https://github.com/kokonut-labs/kokonutui\n */\n\nimport { motion, useReducedMotion } from \"motion/react\";\nimport Image from \"next/image\";\nimport { useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\ninterface Specification {\n label: string;\n value: string;\n}\n\ninterface Product {\n id: string;\n title: string;\n subtitle: string;\n description: string;\n image: string;\n specs: Specification[];\n}\n\nconst products: Product[] = [\n {\n id: \"instant-pay\",\n title: \"Quick Pay\",\n subtitle: \"Instant Transfers\",\n description:\n \"Move money in seconds with bank-grade security and zero surprises.\",\n image: \"/undraw.svg\",\n specs: [\n { label: \"Speed\", value: \"Instant\" },\n { label: \"Security\", value: \"256-bit\" },\n { label: \"Limit\", value: \"$50,000\" },\n { label: \"Fee\", value: \"0.5%\" },\n ],\n },\n {\n id: \"crypto-pay\",\n title: \"Crypto Pay\",\n subtitle: \"Web3 Payments\",\n description:\n \"Accept crypto across every major chain with optimized gas routing.\",\n image:\n \"https://images.unsplash.com/photo-1527443224154-c4a3942d3acf?w=800&auto=format&fit=crop&q=80\",\n specs: [\n { label: \"Network\", value: \"Multi-chain\" },\n { label: \"Gas\", value: \"Optimized\" },\n { label: \"Support\", value: \"24/7\" },\n { label: \"Security\", value: \"Top-tier\" },\n ],\n },\n {\n id: \"business-pay\",\n title: \"Business Pay\",\n subtitle: \"Enterprise Solutions\",\n description:\n \"Built for high-volume teams with custom APIs and premium support.\",\n image:\n \"https://images.unsplash.com/photo-1544244015-0df4b3ffc6b0?w=800&auto=format&fit=crop&q=80\",\n specs: [\n { label: \"Volume\", value: \"Unlimited\" },\n { label: \"API\", value: \"REST/SDK\" },\n { label: \"Support\", value: \"Premium\" },\n { label: \"Features\", value: \"Custom\" },\n ],\n },\n {\n id: \"global-pay\",\n title: \"Global Pay\",\n subtitle: \"International Transfers\",\n description:\n \"Send to 180+ countries with real-time FX and same-day settlement.\",\n image:\n \"https://images.unsplash.com/photo-1629131726692-1accd0c53ce0?w=800&auto=format&fit=crop&q=80\",\n specs: [\n { label: \"Countries\", value: \"180+\" },\n { label: \"FX Rate\", value: \"Real-time\" },\n { label: \"Speed\", value: \"Same-day\" },\n { label: \"Support\", value: \"Local\" },\n ],\n },\n];\n\nconst CARD_WIDTH = 320;\nconst CARD_OVERLAP = 240;\n\ninterface CardProps {\n product: Product;\n index: number;\n totalCards: number;\n isExpanded: boolean;\n reducedMotion: boolean;\n}\n\nconst Card = ({\n product,\n index,\n totalCards,\n isExpanded,\n reducedMotion,\n}: CardProps) => {\n const centerOffset = (totalCards - 1) * 5;\n const defaultX = index * 10 - centerOffset;\n const defaultY = index * 2;\n const defaultRotate = index * 1.5;\n\n const totalExpandedWidth =\n CARD_WIDTH + (totalCards - 1) * (CARD_WIDTH - CARD_OVERLAP);\n const expandedCenterOffset = totalExpandedWidth / 2;\n\n const spreadX =\n index * (CARD_WIDTH - CARD_OVERLAP) - expandedCenterOffset + CARD_WIDTH / 2;\n const spreadRotate = index * 5 - (totalCards - 1) * 2.5;\n\n const collapsedPose = {\n x: defaultX,\n y: defaultY,\n rotate: reducedMotion ? 0 : defaultRotate,\n scale: 1,\n };\n\n const expandedPose = {\n x: spreadX,\n y: 0,\n rotate: reducedMotion ? 0 : spreadRotate,\n scale: 1,\n };\n\n const isSvg = product.image.endsWith(\".svg\");\n\n return (\n \n \n \n {product.specs.map((spec) => (\n \n \n {spec.value}\n \n \n {spec.label}\n \n \n ))}\n \n\n \n \n \n\n \n \n \n {product.title}\n \n \n {product.subtitle}\n \n \n \n {product.description}\n \n \n \n \n );\n};\n\ninterface CardStackProps {\n className?: string;\n}\n\nexport default function CardStackExample({ className }: CardStackProps) {\n const [isExpanded, setIsExpanded] = useState(false);\n const reducedMotion = useReducedMotion() ?? false;\n\n const handleToggle = () => setIsExpanded((prev) => !prev);\n\n return (\n \n {products.map((product, index) => (\n \n ))}\n \n );\n}\n", "path": "/components/kokonutui/card-stack.tsx", "target": "components/kokonutui/card-stack.tsx" } ] }
\n {product.description}\n