{"$schema":"https://ui.shadcn.com/schema/registry-item.json","author":"Eduardo Calvo <educlopez93@gmail.com>","css":{},"dependencies":["motion"],"description":"A 3D CSS book component with perspective transforms and hover animation","devDependencies":[],"files":[{"content":"\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport { useReducedMotion } from \"motion/react\";\nimport type { ReactNode } from \"react\";\n\nexport type BookVariant = \"stripe\" | \"simple\";\n\nexport interface BookProps {\n  /** Additional CSS classes */\n  className?: string;\n  /** Cover accent color (CSS color value) */\n  color?: string;\n  /** Custom illustration for the stripe area (stripe variant only) */\n  illustration?: ReactNode;\n  /** Logo or icon displayed at the bottom of the content area */\n  logo?: ReactNode;\n  /** Title text color override */\n  textColor?: string;\n  /** Book title text displayed on the cover */\n  title: string;\n  /** Visual variant of the book */\n  variant?: BookVariant;\n  /** Book width in pixels */\n  width?: number;\n}\n\nconst BOOK_DEPTH = \"29cqw\";\nconst BOOK_BORDER_RADIUS = \"6px 4px 4px 6px\";\nconst DEFAULT_WIDTH = 196;\nconst DEFAULT_COLOR = \"#e5a00d\";\n\nconst BG_SHADOW =\n  \"linear-gradient(90deg, #fff0 0%, #fff0 12%, #ffffff40 29.25%, #fff0 50.5%, #fff0 75.25%, #ffffff40 91%, #fff0 100%), linear-gradient(90deg, #00000008 0%, #0000001a 12%, #0000 30%, #00000005 50%, #0003 73.5%, #00000080 75.25%, #00000026 85.25%, #0000 100%)\";\n\nconst Book = ({\n  title,\n  variant = \"stripe\",\n  color = DEFAULT_COLOR,\n  textColor,\n  width = DEFAULT_WIDTH,\n  illustration,\n  logo,\n  className,\n}: BookProps) => {\n  const shouldReduceMotion = useReducedMotion();\n  const bookWidth = `${width}px`;\n\n  return (\n    <div\n      className={cn(\"inline-block w-fit\", className)}\n      style={{\n        perspective: \"900px\",\n        // CSS custom properties for internal use\n        [\"--book-width\" as string]: bookWidth,\n        [\"--book-color\" as string]: color,\n        [\"--book-text-color\" as string]: textColor || \"var(--color-foreground)\",\n        [\"--book-depth\" as string]: BOOK_DEPTH,\n        [\"--book-border-radius\" as string]: BOOK_BORDER_RADIUS,\n        [\"--bg-shadow\" as string]: BG_SHADOW,\n      }}\n    >\n      {/* Rotate wrapper - handles 3D rotation on hover */}\n      <div\n        className={cn(\n          \"relative w-fit\",\n          \"[transform-style:preserve-3d]\",\n          \"[container-type:inline-size]\",\n          !shouldReduceMotion && [\n            \"transition-transform duration-[250ms] ease-out\",\n            \"hover:[transform:rotateY(-20deg)_scale(1.066)_translateX(-8px)]\",\n          ]\n        )}\n        style={{\n          aspectRatio: \"49 / 60\",\n          minWidth: bookWidth,\n        }}\n      >\n        {/* Front cover */}\n        <div\n          className={cn(\n            \"absolute flex flex-col\",\n            \"overflow-hidden\",\n            \"[transform:translateZ(0px)]\",\n            \"shadow-[rgba(0,0,0,0.02)_0px_1px_1px,rgba(0,0,0,0.1)_0px_4px_8px_-4px,rgba(0,0,0,0.03)_0px_16px_24px_-8px]\",\n            \"dark:bg-[linear-gradient(rgba(255,255,255,0.1)_0%,rgba(255,255,255,0)_50%,rgba(255,255,255,0)_100%),rgb(31,31,31)]\",\n            \"dark:shadow-[rgba(0,0,0,0.05)_0px_1.8px_3.6px,rgba(0,0,0,0.08)_0px_10.8px_21.6px,rgba(0,0,0,0.1)_0px_-0.9px_inset,rgba(255,255,255,0.1)_0px_1.8px_1.8px_inset,rgba(0,0,0,0.1)_3.6px_0px_3.6px_inset]\"\n          )}\n          style={{\n            background:\n              variant === \"simple\" ? color : \"var(--color-background)\",\n            borderRadius: BOOK_BORDER_RADIUS,\n            height: \"100%\",\n            width: bookWidth,\n          }}\n        >\n          {variant === \"stripe\" ? (\n            <StripeContent\n              illustration={illustration}\n              logo={logo}\n              title={title}\n              width={width}\n            />\n          ) : (\n            <SimpleContent logo={logo} title={title} width={width} />\n          )}\n\n          {/* Border overlay */}\n          <div\n            aria-hidden\n            className={cn(\n              \"pointer-events-none absolute inset-0\",\n              \"border border-black/10\",\n              \"shadow-[rgba(255,255,255,0.3)_0px_1px_2px_inset]\",\n              \"dark:border-0\"\n            )}\n            style={{ borderRadius: \"inherit\" }}\n          />\n        </div>\n\n        {/* Page edges (right side) */}\n        <div\n          aria-hidden\n          className={cn(\n            \"pointer-events-none absolute\",\n            \"bg-[linear-gradient(90deg,rgb(234,234,234)_0%,rgba(0,0,0,0)_70%),linear-gradient(rgb(255,255,255),rgb(250,250,250))]\",\n            \"dark:bg-[linear-gradient(90deg,rgb(60,60,60)_0%,rgba(0,0,0,0)_70%),linear-gradient(rgb(40,40,40),rgb(35,35,35))]\"\n          )}\n          style={{\n            height: \"calc(100% - 6px)\",\n            top: \"3px\",\n            transform: `translateX(calc(${bookWidth} - ${BOOK_DEPTH} / 2 - 3px)) rotateY(90deg) translateX(calc(${BOOK_DEPTH} / 2))`,\n            width: `calc(${BOOK_DEPTH} - 2px)`,\n          }}\n        />\n\n        {/* Back cover */}\n        <div\n          aria-hidden\n          className=\"pointer-events-none absolute left-0\"\n          style={{\n            backgroundColor:\n              variant === \"simple\" ? color : \"var(--color-background)\",\n            borderRadius: BOOK_BORDER_RADIUS,\n            height: \"100%\",\n            transform: `translateZ(calc(-1 * ${BOOK_DEPTH}))`,\n            width: bookWidth,\n          }}\n        />\n      </div>\n    </div>\n  );\n};\n\n/** Stripe variant: color band at top, white body below */\nconst StripeContent = ({\n  title,\n  illustration,\n  logo,\n  width,\n}: {\n  title: string;\n  illustration?: ReactNode;\n  logo?: ReactNode;\n  width: number;\n}) => (\n  <>\n    {/* Colored stripe area */}\n    <div\n      className=\"relative flex w-full flex-1 flex-row items-stretch gap-2 overflow-hidden [transform:translateZ(0px)]\"\n      style={{ background: \"var(--book-color)\" }}\n    >\n      {illustration ? (\n        <div className=\"flex-1 object-cover\">{illustration}</div>\n      ) : (\n        <div className=\"flex-1\" />\n      )}\n      {/* Spine binding overlay on stripe */}\n      <div\n        aria-hidden\n        className=\"absolute inset-0\"\n        style={{\n          background: \"var(--bg-shadow)\",\n          mixBlendMode: \"overlay\",\n        }}\n      />\n    </div>\n\n    {/* Body with title */}\n    <div className=\"flex flex-row items-stretch\">\n      {/* Spine binding on body */}\n      <div\n        aria-hidden\n        className=\"min-w-[8.2%] opacity-20\"\n        style={{ background: \"var(--bg-shadow)\" }}\n      />\n      {/* Content */}\n      <div\n        className=\"flex w-full flex-col justify-between [container-type:inline-size]\"\n        style={{\n          gap: `${(24 / DEFAULT_WIDTH) * width}px`,\n          padding: \"6.1%\",\n        }}\n      >\n        <span\n          className=\"text-balance font-semibold leading-[1.25em] tracking-[-0.02em]\"\n          style={{\n            color: \"var(--book-text-color)\",\n            fontSize: \"10.5cqw\",\n          }}\n        >\n          {title}\n        </span>\n        {logo ? <div>{logo}</div> : null}\n      </div>\n    </div>\n  </>\n);\n\n/** Simple variant: full-color cover with title */\nconst SimpleContent = ({\n  title,\n  logo,\n  width,\n}: {\n  title: string;\n  logo?: ReactNode;\n  width: number;\n}) => (\n  <div className=\"flex h-full w-full flex-row items-stretch\">\n    {/* Spine binding */}\n    <div\n      aria-hidden\n      className=\"min-w-[8.2%]\"\n      style={{\n        background: \"var(--bg-shadow)\",\n        mixBlendMode: \"overlay\",\n        opacity: 1,\n      }}\n    />\n    {/* Content */}\n    <div\n      className=\"flex w-full flex-col justify-between [container-type:inline-size]\"\n      style={{\n        gap: `${(16 / DEFAULT_WIDTH) * width}px`,\n        padding: \"6.1%\",\n      }}\n    >\n      <span\n        className=\"text-balance font-semibold leading-[1.25em] tracking-[-0.02em]\"\n        style={{\n          color: \"var(--book-text-color)\",\n          fontSize: \"12cqw\",\n        }}\n      >\n        {title}\n      </span>\n      {logo ? <div>{logo}</div> : null}\n    </div>\n  </div>\n);\n\nexport default Book;\n","path":"index.tsx","target":"components/smoothui/book/index.tsx","type":"registry:ui"}],"name":"book","registryDependencies":[],"title":"Book","type":"registry:ui"}