{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "split-text-button",
  "type": "registry:ui",
  "title": "Split Text Button",
  "description": "A button whose label rolls character by character on hover or focus, revealing a duplicate line.",
  "author": "WANSTUDIO <https://wanstudio.my>",
  "dependencies": [],
  "files": [
    {
      "path": "registry/wancraft/split-text-button.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nexport interface SplitTextButtonProps extends Omit<React.ComponentPropsWithoutRef<\"button\">, \"children\"> {\n  /** Plain-text label. It is split into characters for the effect and kept intact for assistive technology. */\n  children: string;\n  /** Direction the characters travel on hover / focus. */\n  direction?: \"up\" | \"down\";\n  /** Delay between consecutive characters, in ms. */\n  stagger?: number;\n  /** Duration of each character's transition, in ms. */\n  duration?: number;\n  /** Any CSS easing, e.g. a cubic-bezier(). */\n  easing?: string;\n  /** Which end of the word starts moving first. */\n  staggerFrom?: \"start\" | \"center\" | \"end\";\n  /** Colour of the incoming line. */\n  accent?: string;\n  variant?: \"ink\" | \"outline\" | \"ghost\";\n}\n\n/** Stagger order for character `i` of `n`. */\nfunction staggerIndex(i: number, n: number, from: \"start\" | \"center\" | \"end\") {\n  if (from === \"end\") return n - 1 - i;\n  if (from === \"center\") return Math.round(Math.abs(i - (n - 1) / 2));\n  return i;\n}\n\n/**\n * A button whose label rolls character by character on hover or keyboard focus,\n * revealing a duplicate line. Pure CSS transitions — no animation library required.\n */\nexport const SplitTextButton = React.forwardRef<HTMLButtonElement, SplitTextButtonProps>(function SplitTextButton(\n  {\n    children,\n    direction = \"up\",\n    stagger = 24,\n    duration = 450,\n    easing = \"cubic-bezier(0.16, 1, 0.3, 1)\",\n    staggerFrom = \"start\",\n    accent = \"#FF7448\",\n    variant = \"ink\",\n    className = \"\",\n    style,\n    type = \"button\",\n    ...props\n  },\n  ref,\n) {\n  const chars = Array.from(children);\n\n  // Static class strings so Tailwind can see them.\n  const travel =\n    direction === \"up\"\n      ? \"group-hover:-translate-y-full group-focus-visible:-translate-y-full\"\n      : \"group-hover:translate-y-full group-focus-visible:translate-y-full\";\n  const duplicatePosition = direction === \"up\" ? \"top-full\" : \"bottom-full\";\n\n  const variantClass =\n    variant === \"ink\"\n      ? \"rounded-full bg-zinc-950 px-7 text-white dark:bg-white dark:text-zinc-950\"\n      : variant === \"outline\"\n        ? \"rounded-full border border-zinc-900/15 px-7 text-zinc-900 hover:border-zinc-900/30 dark:border-white/15 dark:text-zinc-100 dark:hover:border-white/30\"\n        : \"rounded-md px-2 text-zinc-900 dark:text-zinc-100\";\n\n  return (\n    <button\n      ref={ref}\n      type={type}\n      className={`group relative inline-flex h-12 select-none items-center justify-center text-sm font-medium tracking-tight transition-[border-color] duration-200 focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-[var(--stb-accent)] disabled:pointer-events-none disabled:opacity-50 ${variantClass} ${className}`}\n      style={{ [\"--stb-accent\" as string]: accent, ...style }}\n      {...props}\n    >\n      {/* The accessible name is the untouched label. */}\n      <span className=\"sr-only\">{children}</span>\n\n      <span aria-hidden=\"true\" className=\"relative flex overflow-hidden whitespace-pre leading-[1.3]\">\n        {chars.map((char, i) => (\n          <span\n            key={i}\n            className={`relative inline-block transition-transform will-change-transform motion-reduce:transition-none ${travel}`}\n            style={{\n              transitionDuration: `${duration}ms`,\n              transitionTimingFunction: easing,\n              transitionDelay: `${staggerIndex(i, chars.length, staggerFrom) * stagger}ms`,\n            }}\n          >\n            {char}\n            <span className={`absolute left-0 ${duplicatePosition} text-[var(--stb-accent)]`}>{char}</span>\n          </span>\n        ))}\n      </span>\n\n      {variant === \"ghost\" && (\n        // Underline that draws in alongside the characters.\n        <span\n          aria-hidden=\"true\"\n          className=\"absolute inset-x-2 bottom-2.5 h-px origin-left scale-x-0 bg-[var(--stb-accent)] transition-transform group-hover:scale-x-100 group-focus-visible:scale-x-100 motion-reduce:transition-none\"\n          style={{ transitionDuration: `${duration + chars.length * stagger}ms`, transitionTimingFunction: easing }}\n        />\n      )}\n    </button>\n  );\n});\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "buttons"
  ],
  "docs": "Documentation: https://wancraft.my/components/split-text-button"
}
