{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "social-button",
  "title": "Social Button",
  "description": "Sign-in buttons for identity providers, including icon-only.",
  "dependencies": [
    "lucide-react@^1.33.0"
  ],
  "registryDependencies": [
    "https://components.exe.xyz/r/utils.json"
  ],
  "files": [
    {
      "path": "src/components/ui/social-button.tsx",
      "content": "import type { MouseEventHandler, ReactNode } from \"react\"\nimport { Apple } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport type SocialBrand =\n  | \"google\"\n  | \"apple\"\n  | \"github\"\n  | \"gitlab\"\n  | \"microsoft\"\n  | \"x\"\n  | \"facebook\"\n  | \"linkedin\"\n  | \"discord\"\n  | \"slack\"\n  | \"figma\"\n  | \"notion\"\n  | \"dropbox\"\n  | \"spotify\"\n  | \"twitch\"\n  | \"reddit\"\n  | \"tiktok\"\n  | \"instagram\"\n  | \"telegram\"\n  | \"whatsapp\"\n  | \"amazon\"\n  | \"bitbucket\"\n  | \"auth0\"\n  | \"okta\"\n  | \"custom\"\n\ntype BrandMeta = {\n  label: string\n  color: string\n  letter: string\n}\n\nconst BRANDS: Record<Exclude<SocialBrand, \"custom\">, BrandMeta> = {\n  google: { label: \"Google\", color: \"#4285F4\", letter: \"G\" },\n  apple: { label: \"Apple\", color: \"#111111\", letter: \"A\" },\n  github: { label: \"GitHub\", color: \"#181717\", letter: \"G\" },\n  gitlab: { label: \"GitLab\", color: \"#E24329\", letter: \"G\" },\n  microsoft: { label: \"Microsoft\", color: \"#00A4EF\", letter: \"M\" },\n  x: { label: \"X\", color: \"#111111\", letter: \"X\" },\n  facebook: { label: \"Facebook\", color: \"#1877F2\", letter: \"f\" },\n  linkedin: { label: \"LinkedIn\", color: \"#0A66C8\", letter: \"in\" },\n  discord: { label: \"Discord\", color: \"#5865F2\", letter: \"D\" },\n  slack: { label: \"Slack\", color: \"#4A154B\", letter: \"S\" },\n  figma: { label: \"Figma\", color: \"#F24E1E\", letter: \"F\" },\n  notion: { label: \"Notion\", color: \"#111111\", letter: \"N\" },\n  dropbox: { label: \"Dropbox\", color: \"#0061FF\", letter: \"D\" },\n  spotify: { label: \"Spotify\", color: \"#1DB954\", letter: \"S\" },\n  twitch: { label: \"Twitch\", color: \"#9146FF\", letter: \"T\" },\n  reddit: { label: \"Reddit\", color: \"#FF4500\", letter: \"R\" },\n  tiktok: { label: \"TikTok\", color: \"#111111\", letter: \"T\" },\n  instagram: { label: \"Instagram\", color: \"#E4405F\", letter: \"I\" },\n  telegram: { label: \"Telegram\", color: \"#26A5E4\", letter: \"T\" },\n  whatsapp: { label: \"WhatsApp\", color: \"#25D366\", letter: \"W\" },\n  amazon: { label: \"Amazon\", color: \"#FF9900\", letter: \"a\" },\n  bitbucket: { label: \"Bitbucket\", color: \"#0052CC\", letter: \"B\" },\n  auth0: { label: \"Auth0\", color: \"#EB5424\", letter: \"0\" },\n  okta: { label: \"Okta\", color: \"#007DC1\", letter: \"O\" },\n}\n\nfunction luminance(hex: string) {\n  const n = hex.replace(\"#\", \"\")\n  if (n.length < 6) return 0\n  const r = Number.parseInt(n.slice(0, 2), 16)\n  const g = Number.parseInt(n.slice(2, 4), 16)\n  const b = Number.parseInt(n.slice(4, 6), 16)\n  return (r * 299 + g * 587 + b * 114) / 1000\n}\n\nfunction GoogleMark({ multi }: { multi: boolean }) {\n  if (!multi) {\n    return (\n      <svg viewBox=\"0 0 16 16\" className=\"size-4\" aria-hidden>\n        <path\n          fill=\"currentColor\"\n          d=\"M8 2a6 6 0 1 0 5.91 7H8.75V7.1h6.13A6 6 0 0 0 8 2Zm0 1.7a4.3 4.3 0 1 1 0 8.6 4.3 4.3 0 0 1 0-8.6Z\"\n        />\n        <rect x=\"8\" y=\"7.1\" width=\"6.4\" height=\"1.8\" fill=\"currentColor\" />\n      </svg>\n    )\n  }\n  return (\n    <svg viewBox=\"0 0 16 16\" className=\"size-4\" aria-hidden>\n      <rect x=\"1.2\" y=\"1.2\" width=\"6.2\" height=\"6.2\" fill=\"#4285F4\" />\n      <rect x=\"8.6\" y=\"1.2\" width=\"6.2\" height=\"6.2\" fill=\"#EA4335\" />\n      <rect x=\"1.2\" y=\"8.6\" width=\"6.2\" height=\"6.2\" fill=\"#FBBC05\" />\n      <rect x=\"8.6\" y=\"8.6\" width=\"6.2\" height=\"6.2\" fill=\"#34A853\" />\n    </svg>\n  )\n}\n\nfunction GithubMark() {\n  return (\n    <svg viewBox=\"0 0 16 16\" className=\"size-4\" aria-hidden>\n      <path\n        fill=\"currentColor\"\n        d=\"M5.1 1.4h5.8l3.7 3.7v5.8l-3.7 3.7H5.1L1.4 10.9V5.1L5.1 1.4Zm2.9 3.3a2.4 2.4 0 1 0 0 4.8 2.4 2.4 0 0 0 0-4.8Z\"\n      />\n    </svg>\n  )\n}\n\nfunction MicrosoftMark({ multi }: { multi: boolean }) {\n  const fills = multi\n    ? [\"#F25022\", \"#7FBA00\", \"#00A4EF\", \"#FFB900\"]\n    : [\"currentColor\", \"currentColor\", \"currentColor\", \"currentColor\"]\n  return (\n    <svg viewBox=\"0 0 16 16\" className=\"size-4\" aria-hidden>\n      <rect x=\"1.4\" y=\"1.4\" width=\"6\" height=\"6\" fill={fills[0]} />\n      <rect x=\"8.6\" y=\"1.4\" width=\"6\" height=\"6\" fill={fills[1]} />\n      <rect x=\"1.4\" y=\"8.6\" width=\"6\" height=\"6\" fill={fills[2]} />\n      <rect x=\"8.6\" y=\"8.6\" width=\"6\" height=\"6\" fill={fills[3]} />\n    </svg>\n  )\n}\n\nfunction LetterMark({\n  letter,\n  color,\n}: {\n  letter: string\n  color?: string\n}) {\n  return (\n    <span\n      aria-hidden\n      className=\"flex size-4 items-center justify-center font-heading text-[10px] leading-none font-semibold\"\n      style={color ? { color } : undefined}\n    >\n      {letter}\n    </span>\n  )\n}\n\nfunction BrandMark({\n  brand,\n  multi,\n  customIcon,\n}: {\n  brand: SocialBrand\n  multi: boolean\n  customIcon?: ReactNode\n}) {\n  if (brand === \"custom\") {\n    return (\n      <span aria-hidden className=\"flex size-4 items-center justify-center\">\n        {customIcon}\n      </span>\n    )\n  }\n  if (brand === \"google\") return <GoogleMark multi={multi} />\n  if (brand === \"apple\") return <Apple className=\"size-4\" aria-hidden />\n  if (brand === \"github\") return <GithubMark />\n  if (brand === \"microsoft\") return <MicrosoftMark multi={multi} />\n  const meta = BRANDS[brand]\n  return (\n    <LetterMark letter={meta.letter} color={multi ? meta.color : undefined} />\n  )\n}\n\nexport type SocialButtonProps = {\n  brand: SocialBrand\n  appearance?: \"colorful\" | \"black\" | \"white\"\n  size?: \"medium\" | \"small\"\n  iconOnly?: boolean\n  fullWidth?: boolean\n  href?: string\n  config?: { icon: ReactNode; label?: string; color?: string }\n  children?: ReactNode\n  disabled?: boolean\n  className?: string\n  type?: \"button\" | \"submit\" | \"reset\"\n  target?: string\n  rel?: string\n  onClick?: MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>\n}\n\nfunction SocialButton({\n  brand,\n  appearance = \"colorful\",\n  size = \"medium\",\n  iconOnly = false,\n  fullWidth = false,\n  href,\n  config,\n  children,\n  disabled = false,\n  className,\n  type = \"button\",\n  target,\n  rel,\n  onClick,\n  ...props\n}: SocialButtonProps) {\n  if (brand === \"custom\" && config?.icon == null && process.env.NODE_ENV !== \"production\") {\n    console.error('SocialButton brand=\"custom\" requires config.icon.')\n  }\n\n  const meta = brand === \"custom\" ? null : BRANDS[brand]\n  const label =\n    config?.label ?? (brand === \"custom\" ? \"SSO\" : (meta?.label ?? \"SSO\"))\n  const text = children ?? `Continue with ${label}`\n  const brandColor = config?.color ?? meta?.color ?? \"#111111\"\n  const multiMark = appearance === \"white\"\n  const lightFill = luminance(brandColor) > 160\n\n  const classes = cn(\n    \"inline-flex shrink-0 items-center justify-center gap-2 rounded-[2px] border border-transparent font-medium whitespace-nowrap transition-colors outline-none select-none\",\n    \"focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50\",\n    \"active:translate-y-px\",\n    \"disabled:pointer-events-none disabled:opacity-50\",\n    \"aria-disabled:pointer-events-none aria-disabled:opacity-50\",\n    \"[&_svg]:pointer-events-none [&_svg]:shrink-0\",\n    size === \"medium\" ? \"h-9 text-sm\" : \"h-8 text-[0.8rem]\",\n    iconOnly\n      ? size === \"medium\"\n        ? \"size-9\"\n        : \"size-8\"\n      : fullWidth\n        ? \"w-full\"\n        : size === \"medium\"\n          ? \"w-[300px]\"\n          : \"w-[250px]\",\n    appearance === \"colorful\" &&\n      (lightFill ? \"text-foreground\" : \"text-white\"),\n    appearance === \"black\" && \"bg-foreground text-background\",\n    appearance === \"white\" &&\n      \"border-border bg-card text-foreground hover:bg-muted\",\n    appearance === \"black\" && \"hover:opacity-90\",\n    className\n  )\n\n  const style =\n    appearance === \"colorful\" ? { backgroundColor: brandColor } : undefined\n\n  const content = (\n    <>\n      <BrandMark brand={brand} multi={multiMark} customIcon={config?.icon} />\n      {iconOnly ? null : <span className=\"min-w-0 truncate\">{text}</span>}\n    </>\n  )\n\n  const ariaLabel = iconOnly ? `Continue with ${label}` : undefined\n\n  if (href != null) {\n    const handleClick: MouseEventHandler<HTMLAnchorElement> = (\n      event\n    ) => {\n      if (disabled) {\n        event.preventDefault()\n        event.stopPropagation()\n        return\n      }\n      onClick?.(event)\n    }\n    return (\n      <a\n        data-slot=\"social-button\"\n        data-brand={brand}\n        data-appearance={appearance}\n        href={href}\n        className={classes}\n        style={style}\n        aria-label={ariaLabel}\n        aria-disabled={disabled || undefined}\n        tabIndex={disabled ? -1 : undefined}\n        target={target}\n        rel={\n          rel ?? (target === \"_blank\" ? \"noreferrer noopener\" : undefined)\n        }\n        onClick={handleClick}\n        {...props}\n      >\n        {content}\n      </a>\n    )\n  }\n\n  return (\n    <button\n      data-slot=\"social-button\"\n      data-brand={brand}\n      data-appearance={appearance}\n      type={type}\n      disabled={disabled}\n      className={classes}\n      style={style}\n      aria-label={ariaLabel}\n      onClick={onClick}\n      {...props}\n    >\n      {content}\n    </button>\n  )\n}\n\nexport { SocialButton, BRANDS }\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}