{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "badges-animated",
  "title": "Animated badges",
  "description": "Styled badges: the Animated set. 5 decorative badges variations (ShimmerBadge, BounceBadge, FlipBadge, SlideBadge, WobbleBadge) 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"
  ],
  "registryDependencies": [
    "@ai2/tokens"
  ],
  "files": [
    {
      "path": "registry/ai2/styled/badges-animated.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Animated badge family: 5 badges built on motion. Colour comes ONLY from tokens, the size is aligned with the base badge scale (+xl). framer-motion (motion/react) is used in every badge; every continuous and hover motion stops under reduced-motion. The sheens derive from primary-foreground so they are visible in both themes. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\nconst box: Record<StyledSize, string> = {\n  sm: \"h-5 gap-1 rounded-md px-1.5 text-xs\",\n  md: \"h-6 gap-1.5 rounded-md px-2 text-xs\",\n  lg: \"h-7 gap-1.5 rounded-lg px-2.5 text-sm\",\n  xl: \"h-8 gap-2 rounded-lg px-3 text-sm\",\n}\n\nconst base =\n  \"relative inline-flex w-fit shrink-0 items-center justify-center whitespace-nowrap font-medium [&_svg]:size-3 [&_svg]:shrink-0 [&_i]:text-xs [&_i]:leading-none\"\n\ntype Props = React.ComponentProps<\"span\"> & { size?: StyledSize }\n\n/* Shimmer: uzerinden surekli gecen token isik supurmesi. */\nexport function ShimmerBadge({ className, size = \"md\", children, ...props }: Props) {\n  const reduce = useReducedMotion()\n  return (\n    <span\n      data-slot=\"styled-badge\"\n      className={cn(base, box[size], \"overflow-hidden bg-primary text-primary-foreground\", className)}\n      {...props}\n    >\n      {!reduce && (\n        <motion.span\n          aria-hidden=\"true\"\n          className=\"pointer-events-none absolute inset-y-0 -left-1/2 w-1/3 -skew-x-12 bg-primary-foreground/25 blur-sm\"\n          animate={{ left: [\"-50%\", \"150%\"] }}\n          transition={{ duration: 2, ease: \"easeInOut\", repeat: Infinity, repeatDelay: 0.8 }}\n        />\n      )}\n      <span className=\"relative\">{children}</span>\n    </span>\n  )\n}\n\n/* Bounce: dongude yumusakca zipar. */\nexport function BounceBadge({ className, size = \"md\", children, ...props }: Props) {\n  const reduce = useReducedMotion()\n  return (\n    <motion.span\n      data-slot=\"styled-badge\"\n      className={cn(base, box[size], \"border border-border bg-secondary text-secondary-foreground\", className)}\n      animate={reduce ? undefined : { y: [0, -3, 0] }}\n      transition={reduce ? undefined : { duration: 1.2, ease: \"easeInOut\", repeat: Infinity }}\n    >\n      {children as React.ReactNode}\n    </motion.span>\n  )\n}\n\n/* Flip: rotates on the Y axis on hover. */\nexport function FlipBadge({ className, size = \"md\", children, ...props }: Props) {\n  const reduce = useReducedMotion()\n  const [on, setOn] = React.useState(false)\n  return (\n    <span\n      className=\"inline-flex [perspective:400px]\"\n      onMouseEnter={() => setOn(true)}\n      onMouseLeave={() => setOn(false)}\n    >\n      <motion.span\n        data-slot=\"styled-badge\"\n        className={cn(base, box[size], \"border border-border bg-secondary text-secondary-foreground [transform-style:preserve-3d]\", className)}\n        animate={reduce ? undefined : { rotateY: on ? 360 : 0 }}\n        transition={{ duration: 0.6, ease: \"easeInOut\" }}\n        {...(props as React.ComponentProps<typeof motion.span>)}\n      >\n        {children as React.ReactNode}\n      </motion.span>\n    </span>\n  )\n}\n\n/* Slide: the label slides on hover and a second label appears from beneath. */\nexport function SlideBadge({\n  className,\n  size = \"md\",\n  children,\n  alt,\n  ...props\n}: Props & { alt?: React.ReactNode }) {\n  const reduce = useReducedMotion()\n  const [on, setOn] = React.useState(false)\n  const active = !reduce && on\n  return (\n    <span\n      data-slot=\"styled-badge\"\n      className={cn(base, box[size], \"overflow-hidden border border-border bg-secondary text-secondary-foreground\", className)}\n      {...props}\n      onMouseEnter={(e) => {\n        setOn(true)\n        props.onMouseEnter?.(e)\n      }}\n      onMouseLeave={(e) => {\n        setOn(false)\n        props.onMouseLeave?.(e)\n      }}\n    >\n      <span className=\"relative inline-flex items-center\">\n        <motion.span className=\"inline-flex items-center\" animate={{ y: active ? \"-130%\" : \"0%\" }} transition={{ duration: 0.3, ease: \"easeOut\" }}>\n          {children}\n        </motion.span>\n        <motion.span\n          aria-hidden=\"true\"\n          className=\"absolute inset-0 inline-flex items-center justify-center\"\n          animate={{ y: active ? \"0%\" : \"130%\" }}\n          transition={{ duration: 0.3, ease: \"easeOut\" }}\n        >\n          {alt ?? children}\n        </motion.span>\n      </span>\n    </span>\n  )\n}\n\n/* Wobble: wobbles on hover. */\nexport function WobbleBadge({ className, size = \"md\", children, ...props }: Props) {\n  const reduce = useReducedMotion()\n  const [on, setOn] = React.useState(false)\n  return (\n    <motion.span\n      data-slot=\"styled-badge\"\n      className={cn(base, box[size], \"border border-border bg-secondary text-secondary-foreground\", className)}\n      animate={!reduce && on ? { rotate: [0, -6, 5, -3, 0] } : { rotate: 0 }}\n      transition={{ duration: 0.5, ease: \"easeInOut\" }}\n      {...(props as React.ComponentProps<typeof motion.span>)}\n      onMouseEnter={() => setOn(true)}\n      onMouseLeave={() => setOn(false)}\n    >\n      {children as React.ReactNode}\n    </motion.span>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/badges-animated.tsx"
    }
  ],
  "categories": [
    "styled",
    "badges"
  ],
  "type": "registry:component"
}