{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "button",
  "title": "Button",
  "description": "RN Pressable 기반 유니버설 버튼 — 변형 6종(default/secondary/destructive/outline/ghost/link) × 사이즈 4종(sm/default/lg/icon), 44px 터치 타깃 보정 포함.",
  "dependencies": [
    "@unikit-bridge/ui",
    "class-variance-authority",
    "clsx",
    "tailwind-merge",
    "tailwindcss"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/button.tsx",
      "content": "// Button — shadcn/ui new-york-v4 button을 UniKit UI 유니버설로 이식（사이클 71 원본값 정비）.\n// 이중 지정 원칙（§0.5）: 배경·테두리는 Pressable, 전경색·밑줄은 내부 Text에 명시.\n// （DOM 브리지에서도 span은 부모 상속이 막히므로 전경색 명시가 양쪽에 필요）\n// 미지원（로드맵）: aria-invalid:*（invalid 상태 없음）·has-［>svg］:*·［&_svg］:*（자식\n// 셀렉터 — 아이콘은 글리프 Text）·dark:*（다크는 토큰 값 전환 아키텍처로 처리）.\n// focus 링은 원본 규격을 준수한다（사이클 72）: ring-3（비대괄호 계단）·ring-ring/50\n// ·무오프셋·focus-visible:border-ring — destructive는 ring-destructive/20.\n// link 변형의 밑줄은 원본대로 hover에서만 — 네이티브는 hover가 없어 밑줄이 없다\n// （상시 밑줄이 필요하면 소비자가 className=\"underline\"을 더한다）.\nimport { forwardRef, type ReactNode } from \"react\";\nimport {\n  Pressable,\n  Text,\n  type BridgePressableRef,\n  type PressableProps,\n} from \"@unikit-bridge/ui/primitives\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils\";\n\n// Pressable（컨테이너） — 원본 base의 shrink-0·transition-all을 포함하고, hover는\n// web: 접두, active（눌림 어포던스 근사）는 양쪽에서 동작한다. focus 링은 원본 규격\n// （ring-3 무오프셋）을 web: 접두로 준수한다.\nconst buttonVariants = cva(\n  \"flex flex-row shrink-0 items-center justify-center gap-2 rounded-md web:select-none web:transition-all web:outline-none web:focus-visible:border-ring web:focus-visible:ring-3 web:focus-visible:ring-ring/50\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-primary web:hover:bg-primary/90 active:bg-primary/90\",\n        secondary: \"bg-secondary web:hover:bg-secondary/80 active:bg-secondary/80\",\n        destructive:\n          \"bg-destructive web:hover:bg-destructive/90 active:bg-destructive/90 web:focus-visible:ring-destructive/20\",\n        // 원본 outline: border bg-background shadow-xs hover:bg-accent\n        // hover:text-accent-foreground — text 스위치는 내부 Text 변형에 이중 지정.\n        outline:\n          \"border border-border bg-background web:hover:bg-accent active:bg-accent\",\n        ghost: \"web:hover:bg-accent active:bg-accent\",\n        link: \"\",\n      },\n      // 원본 사이즈 값 그대로（default h-9 / xs h-6 / sm h-8 / lg h-10 / icon size-9）.\n      // 원본의 has-［>svg］:px-*·svg 크기 기본값（size-3/4）은 자식 셀렉터라 미지원.\n      size: {\n        default: \"h-9 px-4 py-2\",\n        xs: \"h-6 gap-1 rounded-md px-2\",\n        sm: \"h-8 gap-1.5 rounded-md px-3\",\n        lg: \"h-10 rounded-md px-6\",\n        icon: \"size-9\",\n        \"icon-xs\": \"size-6 rounded-md\",\n        \"icon-sm\": \"size-8\",\n        \"icon-lg\": \"size-10\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  },\n);\n\n// 내부 Text — 변형별 전경색. active:* 눌림 전파 — 네이티브는 uniwind（Pressable 상태）, 웹은 <button>의 :active.\nconst buttonTextVariants = cva(\n  \"font-sans text-sm font-medium web:whitespace-nowrap\",\n  {\n    variants: {\n      variant: {\n        default: \"text-primary-foreground\",\n        secondary: \"text-secondary-foreground\",\n        // 원본 text-white의 토큰 동등물（--destructive-foreground = 98% 백색）.\n        destructive: \"text-destructive-foreground\",\n        outline:\n          \"text-foreground active:text-accent-foreground web:hover:text-accent-foreground\",\n        ghost:\n          \"text-foreground active:text-accent-foreground web:hover:text-accent-foreground\",\n        link: \"text-primary underline-offset-4 web:hover:underline\",\n      },\n      // xs·icon-xs의 원본 text-xs — 컨테이너 클래스는 네이티브 Text에 닿지 않아\n      // 내부 Text에 이중 지정（§0.5）한다.\n      size: {\n        default: \"\",\n        xs: \"text-xs\",\n        sm: \"\",\n        lg: \"\",\n        icon: \"\",\n        \"icon-xs\": \"text-xs\",\n        \"icon-sm\": \"\",\n        \"icon-lg\": \"\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  },\n);\n\n// 터치 타깃 44px 보정（§3.1）: hitSlop = max（0, （44 − 시각높이px） / 2） — 각 방향 px.\nconst HIT_SLOP = {\n  xs: 10,\n  sm: 6,\n  default: 4,\n  lg: 2,\n  icon: 4,\n  \"icon-xs\": 10,\n  \"icon-sm\": 6,\n  \"icon-lg\": 2,\n} as const;\n\n// Pressable의 함수형 children（（state） => ReactNode）은 내부 Text 래핑 구조（§3.1）와\n// 양립하지 않으므로 children은 ReactNode로 한정한다（v0.1은 문자열 children 사용）.\nexport type ButtonProps = PressableProps &\n  VariantProps<typeof buttonVariants> & {\n    children?: ReactNode;\n  };\n\nconst Button = forwardRef<BridgePressableRef, ButtonProps>(\n  (\n    { className, variant, size, disabled, hitSlop, children, ...props },\n    ref,\n  ) => {\n    const resolvedSize = size ?? \"default\";\n    return (\n      <Pressable\n        ref={ref}\n        accessibilityRole=\"button\"\n        disabled={disabled}\n        aria-disabled={disabled ? true : undefined}\n        hitSlop={hitSlop ?? HIT_SLOP[resolvedSize]}\n        className={cn(\n          buttonVariants({ variant, size }),\n          disabled && \"web:pointer-events-none opacity-50\",\n          className,\n        )}\n        {...props}\n      >\n        <Text className={cn(buttonTextVariants({ variant, size }))}>\n          {children}\n        </Text>\n      </Pressable>\n    );\n  },\n);\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants, buttonTextVariants };\n",
      "type": "registry:ui",
      "target": "components/unikit/button.tsx"
    }
  ],
  "type": "registry:ui"
}
