{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "arrow-tile-button",
  "type": "registry:ui",
  "title": "Arrow Tile Button",
  "description": "A label slab paired with a detached arrow tile: the tilt variant has the tile knock the slab so its letters topple in a wave away from it and spring upright, and the swap variant rolls the tile to the front as the letters lift in its wake.",
  "dependencies": [],
  "registryDependencies": [],
  "cssVars": {
    "theme": {}
  },
  "files": [
    {
      "path": "components/tweenui/arrow-tile-button.tsx",
      "type": "registry:component",
      "target": "components/tweenui/arrow-tile-button.tsx",
      "content": "import type { ComponentPropsWithRef, CSSProperties } from 'react';\nimport { cn } from '@/lib/utils';\n\nexport interface ArrowTileButtonProps extends ComponentPropsWithRef<'button'> {\n  children: string;\n  variant?: 'tilt' | 'swap';\n}\n\nconst Arrow = ({ className }: { className?: string }) => {\n  return (\n    <svg\n      viewBox=\"0 0 16 16\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth={1.75}\n      strokeLinecap=\"square\"\n      aria-hidden=\"true\"\n      className={cn('size-4 flex-none', className)}\n    >\n      <path d=\"M2.5 8h10M8.5 3.5 13 8l-4.5 4.5\" />\n    </svg>\n  );\n};\n\ninterface Wave {\n  leadMs: number;\n  spreadMs: number;\n}\n\nfunction waveDelay(index: number, count: number, { leadMs, spreadMs }: Wave): CSSProperties {\n  const step = count > 1 ? spreadMs / (count - 1) : 0;\n  const style: CSSProperties & { '--hop-delay': string } = {\n    '--hop-delay': `${leadMs + Math.round((count - 1 - index) * step)}ms`,\n  };\n  return style;\n}\n\nconst toppleChar =\n  'inline-block whitespace-pre transition-none group-[:is(:hover,:focus-visible)]:[translate:0_45%] group-[:is(:hover,:focus-visible)]:[rotate:-14deg] group-[:is(:hover,:focus-visible)]:[scale:1_0.35] group-[:is(:hover,:focus-visible)]:[transform:scale(1,2.8571)_rotate(14deg)_translateY(-45%)] group-[:is(:hover,:focus-visible)]:[transition:translate_145ms_var(--dip)_var(--hop-delay),rotate_145ms_var(--dip)_var(--hop-delay),scale_145ms_var(--dip)_var(--hop-delay),transform_580ms_var(--spring)_calc(var(--hop-delay)+145ms)] motion-reduce:transition-none';\n\nconst liftChar =\n  'inline-block whitespace-pre transition-none group-[:is(:hover,:focus-visible)]:[translate:0_-40%] group-[:is(:hover,:focus-visible)]:[rotate:9deg] group-[:is(:hover,:focus-visible)]:[transform:rotate(-9deg)_translateY(40%)] group-[:is(:hover,:focus-visible)]:[transition:translate_160ms_var(--lift)_var(--hop-delay),rotate_160ms_var(--lift)_var(--hop-delay),transform_560ms_var(--spring)_calc(var(--hop-delay)+160ms)] motion-reduce:transition-none';\n\nconst WaveLabel = ({\n  children,\n  charClassName,\n  wave,\n}: {\n  children: string;\n  charClassName: string;\n  wave: Wave;\n}) => {\n  const chars = Array.from(children);\n  return (\n    <>\n      <span className=\"sr-only\">{children}</span>\n      <span aria-hidden=\"true\">\n        {chars.map((char, index) => (\n          <span key={index} className={charClassName} style={waveDelay(index, chars.length, wave)}>\n            {char}\n          </span>\n        ))}\n      </span>\n    </>\n  );\n};\n\nconst TOPPLE_WAVE: Wave = { leadMs: 140, spreadMs: 225 };\nconst LIFT_WAVE: Wave = { leadMs: 40, spreadMs: 340 };\n\nconst knockRelease =\n  'grid [transition:translate_150ms_var(--soft),rotate_150ms_var(--soft),scale_150ms_var(--soft),transform_150ms_var(--soft)] motion-reduce:transition-none';\n\nconst TiltContent = ({ children }: { children: string }) => {\n  return (\n    <>\n      <span\n        className={cn(\n          knockRelease,\n          'origin-left group-[:is(:hover,:focus-visible)]:[translate:-0.3em_0] group-[:is(:hover,:focus-visible)]:[scale:0.95_1.04] group-[:is(:hover,:focus-visible)]:[transform:scale(1.0526,0.9615)_translateX(0.3em)] group-[:is(:hover,:focus-visible)]:[transition:translate_110ms_var(--dip)_60ms,scale_110ms_var(--dip)_60ms,transform_420ms_170ms_var(--bounce)]'\n        )}\n      >\n        <span aria-hidden=\"true\" className=\"col-start-1 row-start-1 rounded-md bg-[#045f64]\" />\n        <span className=\"relative col-start-1 row-start-1 flex items-center px-5\">\n          <WaveLabel charClassName={toppleChar} wave={TOPPLE_WAVE}>\n            {children}\n          </WaveLabel>\n        </span>\n      </span>\n      <span\n        aria-hidden=\"true\"\n        className={cn(\n          knockRelease,\n          '-ml-px size-11 origin-bottom-left group-[:is(:hover,:focus-visible)]:[translate:-0.2em_0] group-[:is(:hover,:focus-visible)]:[rotate:-12deg] group-[:is(:hover,:focus-visible)]:[transform:rotate(12deg)_translateX(0.2em)] group-[:is(:hover,:focus-visible)]:[transition:translate_120ms_var(--dip),rotate_120ms_var(--dip),transform_400ms_120ms_var(--bounce)]'\n        )}\n      >\n        <span className=\"col-start-1 row-start-1 rounded-md bg-[#045f64]\" />\n        <span className=\"relative col-start-1 row-start-1 grid place-items-center\">\n          <Arrow />\n        </span>\n      </span>\n    </>\n  );\n};\n\nconst SwapContent = ({ children }: { children: string }) => {\n  return (\n    <>\n      <span className=\"flex items-center rounded-md bg-[#c6f56f] px-5 text-[#045f64] transition-[translate] duration-500 ease-(--circ) group-[:is(:hover,:focus-visible)]:translate-x-[2.875rem] motion-reduce:transition-none\">\n        <WaveLabel charClassName={liftChar} wave={LIFT_WAVE}>\n          {children}\n        </WaveLabel>\n      </span>\n      <span\n        aria-hidden=\"true\"\n        className=\"absolute top-0 right-0 z-10 grid size-11 place-items-center rounded-md bg-[#045f64] text-[#c6f56f] transition-[right] duration-500 ease-(--circ) group-[:is(:hover,:focus-visible)]:right-[calc(100%-2.75rem)] motion-reduce:transition-none\"\n      >\n        <Arrow className=\"transition-[rotate] duration-500 ease-(--circ) group-[:is(:hover,:focus-visible)]:-rotate-360 motion-reduce:transition-none\" />\n      </span>\n    </>\n  );\n};\n\nconst variants = {\n  tilt: 'text-[#c6f56f] [--bounce:cubic-bezier(0.34,2.27,0.64,1)] [--dip:cubic-bezier(0.55,0.085,0.68,0.53)] [--soft:cubic-bezier(0.59,1,0.88,1.01)]',\n  swap: 'pr-[2.875rem] [--circ:cubic-bezier(0.785,0.135,0.15,0.86)] [--lift:cubic-bezier(0.33,1,0.68,1)]',\n};\n\nconst ArrowTileButton = ({\n  children,\n  className,\n  variant = 'tilt',\n  type = 'button',\n  ...props\n}: ArrowTileButtonProps) => {\n  return (\n    <button\n      type={type}\n      className={cn(\n        'group relative inline-flex h-11 cursor-pointer items-stretch text-sm font-medium tracking-wide whitespace-nowrap uppercase transition-[scale] duration-150 ease-[cubic-bezier(0.4,0,0.2,1)] select-none [--spring:linear(0,0.406_4.2%,0.855_8.3%,1.161_12.5%,1.273_16.7%,1.234_20.8%,1.125_25%,1.017_29.2%,0.95_33.3%,0.931_37.5%,0.946_41.7%,0.975_45.8%,1_50%,1.014_54.2%,1.017_58.3%,1.012_62.5%,1.005_66.7%,0.999_70.8%,0.996_75%,0.997_83.3%,1_91.7%,1)] focus-visible:ring-2 focus-visible:ring-[#045f64] focus-visible:ring-offset-2 focus-visible:outline-none active:[scale:0.955_0.925] disabled:pointer-events-none disabled:opacity-60 motion-reduce:transition-none dark:ring-offset-[#12161F]',\n        variants[variant],\n        className\n      )}\n      {...props}\n    >\n      {variant === 'swap' ? (\n        <SwapContent>{children}</SwapContent>\n      ) : (\n        <TiltContent>{children}</TiltContent>\n      )}\n    </button>\n  );\n};\n\nexport default ArrowTileButton;\n"
    }
  ],
  "docs": "Docs, props and examples: https://tween-ui.vercel.app/component/arrow-tile-button"
}