{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "kbd-motion",
  "title": "Motion kbd",
  "description": "Styled kbd: the Motion set. 5 decorative kbd variations (PressKbd, PopKbd, FadeKbd, BounceKbd, GlowMotionKbd) 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/kbd-motion.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Motion kbd family: press feedback. Press listens to a real keyboard event (scoped to a single key, removed in the effect cleanup); the others react to hover and tap through framer-motion. Colour comes ONLY from tokens, all motion is disabled with useReducedMotion, and no value depends on time or randomness. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\nconst box: Record<StyledSize, string> = {\n  sm: \"h-5 min-w-5 px-1.5 text-xs\",\n  md: \"h-6 min-w-6 px-2 text-xs\",\n  lg: \"h-7 min-w-7 px-2.5 text-sm\",\n  xl: \"h-8 min-w-8 px-3 text-sm\",\n}\n\nconst base =\n  \"inline-flex w-fit shrink-0 select-none items-center justify-center gap-1 whitespace-nowrap rounded-md border border-border bg-surface-2 font-mono font-medium leading-none text-foreground [&_svg]:size-3 [&_svg]:shrink-0 [&_i]:text-xs [&_i]:leading-none\"\n\nconst spring = { type: \"spring\" as const, stiffness: 520, damping: 22 }\n\ntype Props = React.ComponentProps<\"kbd\"> & { size?: StyledSize }\n\n/* Press: it reflects the pressed state from real keydown/keyup. keyName is scoped to\n   a single key; the listeners are removed in the effect cleanup (no leak). */\nexport function PressKbd({\n  className,\n  size = \"md\",\n  children,\n  keyName = \"k\",\n  ...props\n}: Props & { keyName?: string }) {\n  const [pressed, setPressed] = React.useState(false)\n  const reduce = useReducedMotion()\n\n  React.useEffect(() => {\n    const target = keyName.toLowerCase()\n    const down = (e: KeyboardEvent) => {\n      if (e.key.toLowerCase() === target) setPressed(true)\n    }\n    const up = (e: KeyboardEvent) => {\n      if (e.key.toLowerCase() === target) setPressed(false)\n    }\n    /* Sekme arkaplana giderse keyup gelmeyebilir; durumu sifirla. */\n    const reset = () => setPressed(false)\n\n    window.addEventListener(\"keydown\", down)\n    window.addEventListener(\"keyup\", up)\n    window.addEventListener(\"blur\", reset)\n    return () => {\n      window.removeEventListener(\"keydown\", down)\n      window.removeEventListener(\"keyup\", up)\n      window.removeEventListener(\"blur\", reset)\n    }\n  }, [keyName])\n\n  return (\n    <kbd\n      data-slot=\"styled-kbd\"\n      data-pressed={pressed ? \"true\" : undefined}\n      className={cn(\n        base,\n        box[size],\n        \"shadow-[0_2px_0_0_color-mix(in_oklab,var(--color-foreground)_18%,transparent)]\",\n        \"transition-all duration-(--motion-fast) ease-(--motion-ease) motion-reduce:transition-none\",\n        /* The pressed state is always readable through colour and shadow; the downward movement is added ONLY when reduced-motion is off, so motion goes away without the feedback disappearing. */\n        \"data-[pressed=true]:bg-secondary data-[pressed=true]:shadow-none\",\n        !reduce && \"data-[pressed=true]:translate-y-0.5\",\n        className\n      )}\n      {...props}\n    >\n      {children ?? keyName.toUpperCase()}\n    </kbd>\n  )\n}\n\n/* Pop: grows on hover and pulls back on tap (spring). */\nexport function PopKbd({ className, size = \"md\", children, ...props }: Props) {\n  const reduce = useReducedMotion()\n  return (\n    <motion.kbd\n      data-slot=\"styled-kbd\"\n      className={cn(base, box[size], className)}\n      whileHover={reduce ? undefined : { scale: 1.1 }}\n      whileTap={reduce ? undefined : { scale: 0.92 }}\n      transition={spring}\n      {...(props as React.ComponentProps<typeof motion.kbd>)}\n    >\n      {children ?? \"K\"}\n    </motion.kbd>\n  )\n}\n\n/* Fade: the opacity drops softly on hover and tap. */\nexport function FadeKbd({ className, size = \"md\", children, ...props }: Props) {\n  const reduce = useReducedMotion()\n  return (\n    <motion.kbd\n      data-slot=\"styled-kbd\"\n      className={cn(base, box[size], className)}\n      whileHover={reduce ? undefined : { opacity: 0.7 }}\n      whileTap={reduce ? undefined : { opacity: 0.45 }}\n      transition={{ duration: 0.18, ease: \"easeOut\" }}\n      {...(props as React.ComponentProps<typeof motion.kbd>)}\n    >\n      {children ?? \"K\"}\n    </motion.kbd>\n  )\n}\n\n/* Bounce: bounces down on tap and springs back on release. */\nexport function BounceKbd({ className, size = \"md\", children, ...props }: Props) {\n  const reduce = useReducedMotion()\n  return (\n    <motion.kbd\n      data-slot=\"styled-kbd\"\n      className={cn(base, box[size], className)}\n      whileHover={reduce ? undefined : { y: -3 }}\n      whileTap={reduce ? undefined : { y: 3 }}\n      transition={{ type: \"spring\" as const, stiffness: 700, damping: 12 }}\n      {...(props as React.ComponentProps<typeof motion.kbd>)}\n    >\n      {children ?? \"K\"}\n    </motion.kbd>\n  )\n}\n\n/* GlowMotion: a token glow layer opens on hover and tap (an opacity animation; the colour is a fixed token color-mix shadow). */\nexport function GlowMotionKbd({ className, size = \"md\", children, ...props }: Props) {\n  const reduce = useReducedMotion()\n  return (\n    <motion.kbd\n      data-slot=\"styled-kbd\"\n      className={cn(base, box[size], \"relative\", className)}\n      initial=\"rest\"\n      animate=\"rest\"\n      whileHover={reduce ? undefined : \"lit\"}\n      whileTap={reduce ? undefined : \"lit\"}\n      {...(props as React.ComponentProps<typeof motion.kbd>)}\n    >\n      <motion.span\n        aria-hidden=\"true\"\n        className=\"pointer-events-none absolute inset-0 rounded-md shadow-[0_0_14px_-1px_color-mix(in_oklab,var(--color-info)_75%,transparent)]\"\n        variants={{ rest: { opacity: 0 }, lit: { opacity: 1 } }}\n        transition={{ duration: 0.2, ease: \"easeOut\" }}\n      />\n      <span className=\"relative\">{children ?? \"K\"}</span>\n    </motion.kbd>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/kbd-motion.tsx"
    }
  ],
  "categories": [
    "styled",
    "kbd"
  ],
  "type": "registry:component"
}