{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "flip-card",
  "type": "registry:ui",
  "title": "Flip Card",
  "description": "An accessible two-sided card that flips on click, Enter or Space — with the hidden face made inert.",
  "author": "WANSTUDIO <https://wanstudio.my>",
  "dependencies": [],
  "files": [
    {
      "path": "registry/wancraft/flip-card.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nexport interface FlipCardProps extends Omit<React.ComponentPropsWithoutRef<\"div\">, \"children\"> {\n  /** Content of the front face. */\n  front: React.ReactNode;\n  /** Content of the back face. */\n  back: React.ReactNode;\n  /** Rotation axis: \"y\" flips sideways, \"x\" flips vertically. */\n  axis?: \"y\" | \"x\";\n  /** Flip duration in ms. */\n  duration?: number;\n  /** Also flip while a mouse hovers the card. Click / Enter / Space always work. */\n  flipOnHover?: boolean;\n  /** CSS perspective in px. */\n  perspective?: number;\n  /** Corner radius of both faces in px. */\n  rounded?: number;\n  /** Show a small \"flip\" affordance in the corner of each face. */\n  hint?: boolean;\n  /** Controlled state: true shows the back. */\n  flipped?: boolean;\n  /** Initial state when uncontrolled. */\n  defaultFlipped?: boolean;\n  onFlippedChange?: (flipped: boolean) => void;\n  /** Accessible name of the toggle (its pressed state means \"back is showing\"). */\n  label?: string;\n  /** Classes for the front surface (background, border, colour). Replaces the default styling. */\n  frontClassName?: string;\n  /** Classes for the back surface. Replaces the default (inverted) styling. */\n  backClassName?: string;\n}\n\nconst EASE = \"cubic-bezier(0.7, 0, 0.2, 1)\";\n\nfunction FlipHint() {\n  return (\n    <span\n      aria-hidden=\"true\"\n      className=\"pointer-events-none absolute right-3 top-3 inline-flex items-center gap-1 font-mono text-[10px] uppercase tracking-[0.14em] opacity-50\"\n    >\n      <svg viewBox=\"0 0 16 16\" fill=\"none\" className=\"size-3\">\n        <path d=\"M2.5 8a5.5 5.5 0 0 1 9.6-3.7M13.5 8a5.5 5.5 0 0 1-9.6 3.7\" stroke=\"currentColor\" strokeWidth=\"1.3\" strokeLinecap=\"round\" />\n        <path d=\"M12.6 1.8v2.8H9.8M3.4 14.2v-2.8h2.8\" stroke=\"currentColor\" strokeWidth=\"1.3\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n      </svg>\n      Flip\n    </span>\n  );\n}\n\n/**\n * An accessible two-sided card. A single toggle button (aria-pressed) sits beneath the faces,\n * so clicking anywhere flips while links and buttons inside a face keep working. The hidden\n * face is `inert`, so tab order and the accessibility tree always follow the visible side.\n */\nexport function FlipCard({\n  front,\n  back,\n  axis = \"y\",\n  duration = 700,\n  flipOnHover = false,\n  perspective = 1200,\n  rounded = 20,\n  hint = true,\n  flipped: flippedProp,\n  defaultFlipped = false,\n  onFlippedChange,\n  label = \"Show back side\",\n  frontClassName = \"border border-zinc-900/10 bg-white text-zinc-900 dark:border-white/10 dark:bg-[#1b1f23] dark:text-zinc-100\",\n  backClassName = \"border border-zinc-950 bg-zinc-950 text-zinc-100 dark:border-white/10 dark:bg-[#F5F2EB] dark:text-zinc-900\",\n  className = \"\",\n  style,\n  onPointerEnter,\n  onPointerLeave,\n  ...props\n}: FlipCardProps) {\n  const [internal, setInternal] = React.useState(defaultFlipped);\n  const flipped = flippedProp ?? internal;\n\n  const setFlipped = (next: boolean) => {\n    if (next === flipped) return;\n    if (flippedProp === undefined) setInternal(next);\n    onFlippedChange?.(next);\n  };\n\n  const rotate = axis === \"y\" ? \"rotateY\" : \"rotateX\";\n  const faceBase =\n    \"relative [grid-area:1/1] [backface-visibility:hidden] [-webkit-backface-visibility:hidden] motion-reduce:transition-opacity motion-reduce:duration-200 \" +\n    // Faces ignore clicks (they fall through to the toggle) except for their own controls.\n    \"pointer-events-none [&_a]:pointer-events-auto [&_button]:pointer-events-auto [&_input]:pointer-events-auto [&_select]:pointer-events-auto [&_textarea]:pointer-events-auto [&_summary]:pointer-events-auto\";\n\n  return (\n    <div\n      className={`relative ${className}`}\n      style={{ perspective, ...style }}\n      onPointerEnter={(event) => {\n        onPointerEnter?.(event);\n        if (flipOnHover && event.pointerType === \"mouse\") setFlipped(true);\n      }}\n      onPointerLeave={(event) => {\n        onPointerLeave?.(event);\n        if (flipOnHover && event.pointerType === \"mouse\") setFlipped(false);\n      }}\n      {...props}\n    >\n      <button\n        type=\"button\"\n        aria-pressed={flipped}\n        aria-label={label}\n        onClick={() => setFlipped(!flipped)}\n        className=\"absolute inset-0 z-0 cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-zinc-500 dark:focus-visible:outline-zinc-400\"\n        style={{ borderRadius: rounded }}\n      />\n\n      <div\n        className=\"pointer-events-none relative z-10 grid h-full transition-transform motion-reduce:![transform:none] motion-reduce:transition-none\"\n        style={{\n          transformStyle: \"preserve-3d\",\n          transform: `${rotate}(${flipped ? (axis === \"y\" ? 180 : -180) : 0}deg)`,\n          transitionDuration: `${duration}ms`,\n          transitionTimingFunction: EASE,\n        }}\n      >\n        <div\n          inert={flipped}\n          aria-hidden={flipped || undefined}\n          className={`${faceBase} ${flipped ? \"motion-reduce:opacity-0\" : \"\"} ${frontClassName}`}\n          style={{ borderRadius: rounded }}\n        >\n          {front}\n          {hint && <FlipHint />}\n        </div>\n        <div\n          inert={!flipped}\n          aria-hidden={!flipped || undefined}\n          className={`${faceBase} motion-reduce:![transform:none] ${flipped ? \"\" : \"motion-reduce:opacity-0\"} ${backClassName}`}\n          style={{ borderRadius: rounded, transform: `${rotate}(180deg)` }}\n        >\n          {back}\n          {hint && <FlipHint />}\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "cards"
  ],
  "docs": "Documentation: https://wancraft.my/components/flip-card"
}
