{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "button",
  "type": "registry:ui",
  "title": "Button",
  "description": "Fluent 2-styled button with shadcn APIs. Six variants (default, secondary, outline, ghost, destructive, link) and six sizes, brand state ramp, asChild support, and a Fluent double-stroke focus ring.",
  "author": "graundtech <https://github.com/graundtech/fluent2-react-kit>",
  "dependencies": [
    "class-variance-authority",
    "@radix-ui/react-slot"
  ],
  "registryDependencies": [
    "https://fluent2-react-kit.graund.io/r/utils.json"
  ],
  "files": [
    {
      "path": "components/ui/button.tsx",
      "type": "registry:ui",
      "target": "components/ui/button.tsx",
      "content": "import { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type { ComponentProps } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Button — Fluent 2-styled, shadcn-API button.\n *\n * Aesthetics follow Fluent 2 (32px medium height, 14px/600 label, `rounded-md`\n * = 4px, flat surfaces, brand state ramp for the primary); the prop surface\n * (`variant`, `size`, `asChild`, `buttonVariants`) follows shadcn/ui.\n *\n * Server-safe: no `\"use client\"`, no hooks/handlers — the React import is\n * type-only, so the file can be dropped straight into an RSC tree.\n */\nconst buttonVariants = cva(\n  [\n    // layout\n    \"inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap\",\n    \"rounded-md font-semibold select-none cursor-pointer\",\n    // motion — token duration + Fluent easyEase curve; honors prefers-reduced-motion\n    // automatically (the duration tokens collapse to 0.01ms in tokens.css).\n    \"outline-none transition-colors duration-fast ease-ease\",\n    // disabled — opacity read (uniform across every variant/component; see conventions doc)\n    \"disabled:pointer-events-none disabled:opacity-50\",\n    // focus — Fluent double-stroke approximation: 2px brand ring + 2px surface-colored gap\n    \"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n    // invalid — shadcn aria-invalid treatment\n    \"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40\",\n    // icon defaults\n    \"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n  ],\n  {\n    variants: {\n      variant: {\n        // Fluent primary — brand fill, flat, full brand state ramp (spec §2.5).\n        // rest brand-80 → hover brand-70 → pressed brand-60 (light);\n        // rest brand-70 → hover brand-80 → pressed brand-60 (dark).\n        default:\n          \"bg-primary text-primary-foreground hover:bg-brand-70 active:bg-brand-60 dark:hover:bg-brand-80\",\n        // Fluent danger — red fill. No red ramp is exposed, so hover/pressed\n        // step via opacity; the focus ring switches to destructive.\n        destructive:\n          \"bg-destructive text-destructive-foreground hover:bg-destructive/90 active:bg-destructive/80 focus-visible:ring-destructive\",\n        // Fluent standard/neutral — filled neutral surface + border.\n        secondary:\n          \"border border-border bg-secondary text-secondary-foreground hover:bg-accent active:bg-input dark:hover:bg-input dark:active:bg-input\",\n        // Fluent outline — transparent + border, neutral hover.\n        outline:\n          \"border border-input bg-transparent hover:bg-accent hover:text-accent-foreground active:bg-accent/80 dark:bg-input/30 dark:hover:bg-input/50 dark:active:bg-input/70\",\n        // Fluent subtle — transparent, neutral hover only.\n        ghost:\n          \"hover:bg-accent hover:text-accent-foreground active:bg-accent/80 dark:hover:bg-accent/50 dark:active:bg-accent/70\",\n        // Fluent link — brand text, underline on hover. Color ramp matches\n        // Fluent BrandForegroundLink (rest brand-70 → hover brand-60 → pressed\n        // brand-50 light; brand-100 → 110 → 120 dark), kept in sync with the\n        // Link component; the button keeps its own font-semibold weight.\n        link: \"text-brand-70 underline-offset-4 hover:text-brand-60 hover:underline active:text-brand-50 dark:text-brand-100 dark:hover:text-brand-110 dark:active:text-brand-120\",\n      },\n      size: {\n        // Fluent medium (default) — 32px height, 12px horizontal padding, 14px text.\n        default: \"h-8 px-3 text-sm has-[>svg]:px-2.5\",\n        // Fluent small — 24px height, 8px padding, 12px text, tighter gap.\n        sm: \"h-6 gap-1.5 px-2 text-xs has-[>svg]:px-1.5\",\n        // Fluent large — 40px height, 16px padding, 16px text.\n        lg: \"h-10 px-4 text-base has-[>svg]:px-3.5\",\n        // Square icon-only buttons at each height.\n        \"icon-sm\": \"size-6\",\n        icon: \"size-8\",\n        \"icon-lg\": \"size-10\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n);\n\nfunction Button({\n  className,\n  variant = \"default\",\n  size = \"default\",\n  asChild = false,\n  ...props\n}: ComponentProps<\"button\"> &\n  VariantProps<typeof buttonVariants> & {\n    asChild?: boolean;\n  }) {\n  const Comp = asChild ? Slot : \"button\";\n\n  return (\n    <Comp\n      data-slot=\"button\"\n      data-variant={variant}\n      data-size={size}\n      className={cn(buttonVariants({ variant, size, className }))}\n      {...props}\n    />\n  );\n}\n\nexport { Button, buttonVariants };\n"
    }
  ]
}
