{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "empty-motion",
  "title": "Motion empty",
  "description": "Styled empty: the Motion set. 5 decorative empty variations (FloatEmpty, FadeEmpty, PopEmpty, BounceEmpty, DrawEmpty) on the ai2 token system, powered by framer-motion, reduced-motion aware and sized sm to xl. Part of the free styled layer.",
  "dependencies": [
    "motion@^12.42.2",
    "lucide-react@^1.23.0"
  ],
  "registryDependencies": [
    "@ai2/tokens"
  ],
  "files": [
    {
      "path": "registry/ai2/styled/empty-motion.tsx",
      "content": "\"use client\"\n\nimport type * as React from \"react\"\nimport { CloudSun, Feather, Inbox, Sparkles } from \"lucide-react\"\nimport { motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Motion empty family: 5 empty states carrying an animated entrance. Gliding,\n   staggered fade, pop, bounce and an SVG draw. All of them are gated on\n   useReducedMotion(): in reduced mode there is no transform or progressing\n   animation, only content that appears instantly; DrawEmpty falls back to the\n   finished static shape in reduced mode. Color comes ONLY from semantic tokens\n   (transparency via color-mix), size = padding + icon/text scale. Deterministic:\n   no Date.now() or Math.random(). Renders with no props too. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\ntype Scale = {\n  root: string\n  icon: string\n  badge: string\n  title: string\n  description: string\n}\n\nconst scales: Record<StyledSize, Scale> = {\n  sm: {\n    root: \"gap-2 p-6\",\n    icon: \"size-6\",\n    badge: \"size-12\",\n    title: \"text-sm\",\n    description: \"text-xs\",\n  },\n  md: {\n    root: \"gap-3 p-8\",\n    icon: \"size-8\",\n    badge: \"size-16\",\n    title: \"text-base\",\n    description: \"text-sm\",\n  },\n  lg: {\n    root: \"gap-3.5 p-10\",\n    icon: \"size-10\",\n    badge: \"size-20\",\n    title: \"text-lg\",\n    description: \"text-sm\",\n  },\n  xl: {\n    root: \"gap-4 p-12\",\n    icon: \"size-12\",\n    badge: \"size-24\",\n    title: \"text-xl\",\n    description: \"text-base\",\n  },\n}\n\ninterface EmptyProps {\n  className?: string\n  size?: StyledSize\n  icon?: React.ReactNode\n  title?: React.ReactNode\n  description?: React.ReactNode\n  action?: React.ReactNode\n}\n\nconst rootBase = \"flex flex-col items-center justify-center text-center\"\n\nconst iconBase = \"[&_i]:leading-none [&_svg]:size-full\"\n\nconst badgeBase =\n  \"flex items-center justify-center rounded-full border text-primary [background:color-mix(in_oklab,var(--color-primary)_12%,transparent)] [border-color:color-mix(in_oklab,var(--color-primary)_28%,transparent)]\"\n\n/* FloatEmpty: rozet yavasca asagi yukari suzulur, metin fade ile girer. */\nexport function FloatEmpty({\n  className,\n  size = \"md\",\n  icon,\n  title = \"Nothing floating by\",\n  description = \"New items drift in here as soon as they are created.\",\n  action,\n}: EmptyProps) {\n  const s = scales[size]\n  const reduce = useReducedMotion()\n  return (\n    <div data-slot=\"styled-empty\" className={cn(rootBase, s.root, className)}>\n      <motion.span\n        aria-hidden=\"true\"\n        className={cn(badgeBase, s.badge)}\n        initial={reduce ? { opacity: 0 } : { opacity: 0, y: 8 }}\n        animate={\n          reduce ? { opacity: 1 } : { opacity: 1, y: [0, -6, 0] }\n        }\n        transition={\n          reduce\n            ? { duration: 0.2 }\n            : {\n                opacity: { duration: 0.3, ease: \"easeOut\" },\n                y: { duration: 3, ease: \"easeInOut\", repeat: Infinity },\n              }\n        }\n      >\n        <span className={cn(iconBase, s.icon)}>{icon ?? <Feather />}</span>\n      </motion.span>\n      <motion.p\n        className={cn(\"font-semibold text-foreground\", s.title)}\n        initial={{ opacity: 0 }}\n        animate={{ opacity: 1 }}\n        transition={{ duration: 0.3, delay: reduce ? 0 : 0.1 }}\n      >\n        {title}\n      </motion.p>\n      {description ? (\n        <motion.p\n          className={cn(\"max-w-prose text-muted-foreground\", s.description)}\n          initial={{ opacity: 0 }}\n          animate={{ opacity: 1 }}\n          transition={{ duration: 0.3, delay: reduce ? 0 : 0.18 }}\n        >\n          {description}\n        </motion.p>\n      ) : null}\n      {action ? <div className=\"mt-2\">{action}</div> : null}\n    </div>\n  )\n}\n\n/* FadeEmpty: the icon, title and description enter with a staggered fade. In reduced\n   mode there is no slide, only a fade. */\nexport function FadeEmpty({\n  className,\n  size = \"md\",\n  icon,\n  title = \"This view is empty\",\n  description = \"Everything you add will fade into place right here.\",\n  action,\n}: EmptyProps) {\n  const s = scales[size]\n  const reduce = useReducedMotion()\n  const item = {\n    hidden: reduce ? { opacity: 0 } : { opacity: 0, y: 10 },\n    show: reduce ? { opacity: 1 } : { opacity: 1, y: 0 },\n  }\n  return (\n    <motion.div\n      data-slot=\"styled-empty\"\n      className={cn(rootBase, s.root, className)}\n      initial=\"hidden\"\n      animate=\"show\"\n      variants={{\n        hidden: {},\n        show: { transition: { staggerChildren: reduce ? 0 : 0.08 } },\n      }}\n    >\n      <motion.span\n        aria-hidden=\"true\"\n        className={cn(badgeBase, s.badge)}\n        variants={item}\n        transition={{ duration: 0.3, ease: \"easeOut\" }}\n      >\n        <span className={cn(iconBase, s.icon)}>{icon ?? <CloudSun />}</span>\n      </motion.span>\n      <motion.p\n        className={cn(\"font-semibold text-foreground\", s.title)}\n        variants={item}\n        transition={{ duration: 0.3, ease: \"easeOut\" }}\n      >\n        {title}\n      </motion.p>\n      {description ? (\n        <motion.p\n          className={cn(\"max-w-prose text-muted-foreground\", s.description)}\n          variants={item}\n          transition={{ duration: 0.3, ease: \"easeOut\" }}\n        >\n          {description}\n        </motion.p>\n      ) : null}\n      {action ? (\n        <motion.div className=\"mt-2\" variants={item}>\n          {action}\n        </motion.div>\n      ) : null}\n    </motion.div>\n  )\n}\n\n/* PopEmpty: rozet spring ile buyuyerek girer (scale pop). */\nexport function PopEmpty({\n  className,\n  size = \"md\",\n  icon,\n  title = \"All caught up\",\n  description = \"There is nothing left in this list right now.\",\n  action,\n}: EmptyProps) {\n  const s = scales[size]\n  const reduce = useReducedMotion()\n  return (\n    <div data-slot=\"styled-empty\" className={cn(rootBase, s.root, className)}>\n      <motion.span\n        aria-hidden=\"true\"\n        className={cn(badgeBase, s.badge)}\n        initial={reduce ? { opacity: 0 } : { opacity: 0, scale: 0.6 }}\n        animate={reduce ? { opacity: 1 } : { opacity: 1, scale: 1 }}\n        transition={\n          reduce\n            ? { duration: 0.2 }\n            : { type: \"spring\" as const, stiffness: 420, damping: 18 }\n        }\n      >\n        <span className={cn(iconBase, s.icon)}>{icon ?? <Sparkles />}</span>\n      </motion.span>\n      <motion.p\n        className={cn(\"font-semibold text-foreground\", s.title)}\n        initial={{ opacity: 0 }}\n        animate={{ opacity: 1 }}\n        transition={{ duration: 0.3, delay: reduce ? 0 : 0.12 }}\n      >\n        {title}\n      </motion.p>\n      {description ? (\n        <motion.p\n          className={cn(\"max-w-prose text-muted-foreground\", s.description)}\n          initial={{ opacity: 0 }}\n          animate={{ opacity: 1 }}\n          transition={{ duration: 0.3, delay: reduce ? 0 : 0.2 }}\n        >\n          {description}\n        </motion.p>\n      ) : null}\n      {action ? <div className=\"mt-2\">{action}</div> : null}\n    </div>\n  )\n}\n\n/* BounceEmpty: the badge falls from above and settles with a hop (a bouncy\n   spring). */\nexport function BounceEmpty({\n  className,\n  size = \"md\",\n  icon,\n  title = \"Your inbox is clear\",\n  description = \"New messages land here the moment they arrive.\",\n  action,\n}: EmptyProps) {\n  const s = scales[size]\n  const reduce = useReducedMotion()\n  return (\n    <div data-slot=\"styled-empty\" className={cn(rootBase, s.root, className)}>\n      <motion.span\n        aria-hidden=\"true\"\n        className={cn(badgeBase, s.badge)}\n        initial={reduce ? { opacity: 0 } : { opacity: 0, y: -28 }}\n        animate={reduce ? { opacity: 1 } : { opacity: 1, y: 0 }}\n        transition={\n          reduce\n            ? { duration: 0.2 }\n            : { type: \"spring\" as const, stiffness: 500, damping: 12, mass: 0.8 }\n        }\n      >\n        <span className={cn(iconBase, s.icon)}>{icon ?? <Inbox />}</span>\n      </motion.span>\n      <motion.p\n        className={cn(\"font-semibold text-foreground\", s.title)}\n        initial={{ opacity: 0 }}\n        animate={{ opacity: 1 }}\n        transition={{ duration: 0.3, delay: reduce ? 0 : 0.16 }}\n      >\n        {title}\n      </motion.p>\n      {description ? (\n        <motion.p\n          className={cn(\"max-w-prose text-muted-foreground\", s.description)}\n          initial={{ opacity: 0 }}\n          animate={{ opacity: 1 }}\n          transition={{ duration: 0.3, delay: reduce ? 0 : 0.24 }}\n        >\n          {description}\n        </motion.p>\n      ) : null}\n      {action ? <div className=\"mt-2\">{action}</div> : null}\n    </div>\n  )\n}\n\n/* DrawEmpty: bos kutu illustrasyonu SVG stroke ile cizilir (pathLength 0 -> 1).\n   Reduced modda cizim atlanir ve sekil bastan CIZILMIS/sabit gorunur. */\nexport function DrawEmpty({\n  className,\n  size = \"md\",\n  icon,\n  title = \"Nothing packed in here\",\n  description = \"This box is empty. Add your first item to fill it up.\",\n  action,\n}: EmptyProps) {\n  const s = scales[size]\n  const reduce = useReducedMotion()\n  const draw = {\n    initial: { pathLength: reduce ? 1 : 0, opacity: reduce ? 1 : 0 },\n    animate: { pathLength: 1, opacity: 1 },\n  }\n  return (\n    <div data-slot=\"styled-empty\" className={cn(rootBase, s.root, className)}>\n      <span aria-hidden=\"true\" className={cn(\"text-primary\", s.badge)}>\n        {icon ? (\n          <span className={cn(iconBase, \"flex size-full items-center justify-center\")}>\n            {icon}\n          </span>\n        ) : (\n          <svg\n            viewBox=\"0 0 64 64\"\n            fill=\"none\"\n            stroke=\"currentColor\"\n            strokeWidth={2.5}\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n            className=\"size-full\"\n          >\n            {/* Kutu govdesi */}\n            <motion.path\n              d=\"M10 22 L32 12 L54 22 L54 46 L32 56 L10 46 Z\"\n              initial={draw.initial}\n              animate={draw.animate}\n              transition={\n                reduce\n                  ? { duration: 0 }\n                  : { duration: 1, ease: \"easeInOut\" }\n              }\n            />\n            {/* Orta dikey ve ust catal cizgileri */}\n            <motion.path\n              d=\"M10 22 L32 32 L54 22 M32 32 L32 56\"\n              initial={draw.initial}\n              animate={draw.animate}\n              transition={\n                reduce\n                  ? { duration: 0 }\n                  : { duration: 0.8, ease: \"easeInOut\", delay: 0.6 }\n              }\n            />\n          </svg>\n        )}\n      </span>\n      <motion.p\n        className={cn(\"font-semibold text-foreground\", s.title)}\n        initial={{ opacity: 0 }}\n        animate={{ opacity: 1 }}\n        transition={{ duration: 0.3, delay: reduce ? 0 : 1.2 }}\n      >\n        {title}\n      </motion.p>\n      {description ? (\n        <motion.p\n          className={cn(\"max-w-prose text-muted-foreground\", s.description)}\n          initial={{ opacity: 0 }}\n          animate={{ opacity: 1 }}\n          transition={{ duration: 0.3, delay: reduce ? 0 : 1.3 }}\n        >\n          {description}\n        </motion.p>\n      ) : null}\n      {action ? <div className=\"mt-2\">{action}</div> : null}\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/empty-motion.tsx"
    }
  ],
  "categories": [
    "styled",
    "empty"
  ],
  "type": "registry:component"
}