{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "author-card",
  "title": "Author Card",
  "description": "Author identity with an initials fallback, biography, profile links, and a follow toggle.",
  "dependencies": [
    "class-variance-authority",
    "lucide-react"
  ],
  "registryDependencies": [
    "avatar",
    "button",
    "https://uaiblocks.dev/r/uai-theme.json",
    "https://uaiblocks.dev/r/uai-utils.json"
  ],
  "files": [
    {
      "path": "src/registry/uai/components/author-card.tsx",
      "content": "\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { Check, Plus } from \"lucide-react\";\nimport { type ComponentProps, createContext, useContext, useId, useState } from \"react\";\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\nimport { Button } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/uai-utils\";\n\nexport const AUTHOR_CARD_VARIANTS = [\"card\", \"inline\", \"compact\"] as const;\nexport type AuthorCardVariant = (typeof AUTHOR_CARD_VARIANTS)[number];\nexport type AuthorCardProps = ComponentProps<\"article\"> & { variant?: AuthorCardVariant };\ntype AuthorContext = { id: string; variant: AuthorCardVariant };\nconst Context = createContext<AuthorContext | null>(null);\nfunction useAuthor(part: string) {\n  const context = useContext(Context);\n  if (!context) throw new Error(`${part} must be used within AuthorCard`);\n  return context;\n}\nconst actionClass =\n  \"transition-[background-color,color,filter,scale] duration-[120ms,120ms,120ms,140ms] ease-[ease-out,ease-out,ease-out,cubic-bezier(0.23,1,0.32,1)] focus-visible:ring-0 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:outline-solid active:scale-[0.97] motion-reduce:transition-none motion-reduce:active:scale-100\";\nconst mixHover = \"hover:bg-[color-mix(in_oklab,var(--secondary)_85%,var(--foreground))]\";\nfunction initials(name: string) {\n  return name\n    .trim()\n    .split(/\\s+/)\n    .slice(0, 2)\n    .map((word) => word.charAt(0))\n    .join(\"\")\n    .toUpperCase();\n}\n\nconst authorCardVariants = cva(\n  \"grid min-w-0 items-start gap-x-3 text-[13px]/[18px] text-card-foreground\",\n  {\n    variants: {\n      variant: {\n        card: \"grid-cols-[minmax(0,1fr)] gap-y-3 rounded-[14px] border bg-card p-4.5\",\n        inline: \"grid-cols-[auto_minmax(0,1fr)] gap-y-2 rounded-[14px] border-0 bg-transparent p-0\",\n        compact: \"grid-cols-[minmax(0,1fr)] gap-y-2 rounded-xl border bg-card p-3\",\n      },\n    },\n  },\n);\n\nexport function AuthorCard({ variant = \"card\", className, children, ...props }: AuthorCardProps) {\n  const id = useId();\n  return (\n    <Context.Provider value={{ id, variant }}>\n      <article\n        aria-labelledby={`${id}-name`}\n        data-slot=\"author-card\"\n        data-variant={variant}\n        className={cn(authorCardVariants({ variant }), className)}\n        {...props}\n      >\n        {children}\n      </article>\n    </Context.Provider>\n  );\n}\n\nconst avatarSizes = {\n  card: \"size-10 text-[13px]\",\n  inline: \"row-span-3 size-9 text-[12px]\",\n  compact: \"size-7 text-[11px]\",\n};\n\nexport function AuthorCardAvatar({\n  name,\n  src,\n  className,\n  ...props\n}: Omit<ComponentProps<\"span\">, \"children\"> & { name: string; src?: string }) {\n  const { variant } = useAuthor(\"AuthorCardAvatar\");\n  // Radix shows the image only once it loads and falls back to initials otherwise.\n  return (\n    <Avatar\n      aria-hidden=\"true\"\n      data-slot=\"author-card-avatar\"\n      className={cn(\n        \"grid shrink-0 place-items-center overflow-hidden rounded-full bg-muted font-medium text-muted-foreground shadow-[0_0_0_1px_oklch(1_0_0/0.08)]\",\n        avatarSizes[variant],\n        className,\n      )}\n      {...props}\n    >\n      {src ? <AvatarImage src={src} alt=\"\" className=\"object-cover\" /> : null}\n      <AvatarFallback className=\"text-[length:inherit]\">{initials(name)}</AvatarFallback>\n    </Avatar>\n  );\n}\n\nexport function AuthorCardHeader({ className, ...props }: ComponentProps<\"div\">) {\n  const { variant } = useAuthor(\"AuthorCardHeader\");\n  return (\n    <div\n      data-slot=\"author-card-header\"\n      className={cn(\n        \"flex min-w-0 flex-wrap items-center justify-between gap-3\",\n        variant === \"inline\" && \"col-start-2\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport function AuthorCardName({ className, ...props }: ComponentProps<\"p\">) {\n  const { id, variant } = useAuthor(\"AuthorCardName\");\n  return (\n    <p\n      data-slot=\"author-card-name\"\n      className={cn(\n        \"m-0 font-medium\",\n        variant === \"compact\" ? \"text-[13px]/[18px]\" : \"text-[14px]/5\",\n        className,\n      )}\n      {...props}\n      id={`${id}-name`}\n    />\n  );\n}\n\nexport function AuthorCardRole({ className, ...props }: ComponentProps<\"p\">) {\n  useAuthor(\"AuthorCardRole\");\n  return (\n    <p\n      data-slot=\"author-card-role\"\n      className={cn(\"m-0 text-xs/4 text-subtle-foreground\", className)}\n      {...props}\n    />\n  );\n}\n\nexport function AuthorCardBio({ className, ...props }: ComponentProps<\"p\">) {\n  const { variant } = useAuthor(\"AuthorCardBio\");\n  return (\n    <p\n      data-slot=\"author-card-bio\"\n      className={cn(\n        \"m-0 text-pretty text-muted-foreground\",\n        variant === \"compact\" ? \"text-[12.5px]/[18px]\" : \"text-[13px]/[19px]\",\n        variant === \"inline\" && \"col-start-2\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport function AuthorCardLinks({\n  \"aria-label\": label = \"Profiles\",\n  className,\n  ...props\n}: ComponentProps<\"ul\">) {\n  const { variant } = useAuthor(\"AuthorCardLinks\");\n  return (\n    <ul\n      aria-label={label}\n      data-slot=\"author-card-links\"\n      className={cn(\n        \"m-0 flex list-none flex-wrap gap-1 p-0\",\n        variant === \"inline\" && \"col-start-2\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport function AuthorCardLink({ className, children, ...props }: ComponentProps<\"a\">) {\n  const { variant } = useAuthor(\"AuthorCardLink\");\n  return (\n    <li className=\"flex\">\n      <Button\n        asChild\n        variant=\"secondary\"\n        size=\"sm\"\n        className={cn(\n          actionClass,\n          mixHover,\n          \"inline-flex items-center gap-1.5 whitespace-nowrap rounded-full bg-secondary px-2.5 text-[12px] font-medium text-muted-foreground tabular-nums no-underline hover:text-foreground has-[>svg]:px-2.5\",\n          variant === \"compact\" ? \"h-6\" : \"h-6.5\",\n          className,\n        )}\n      >\n        <a data-slot=\"author-card-link\" {...props}>\n          {children}\n        </a>\n      </Button>\n    </li>\n  );\n}\n\nexport type AuthorCardFollowProps = Omit<ComponentProps<\"button\">, \"type\"> & {\n  pressed?: boolean;\n  defaultPressed?: boolean;\n  onPressedChange?: (pressed: boolean) => void;\n};\nexport function AuthorCardFollow({\n  pressed,\n  defaultPressed = false,\n  onPressedChange,\n  onClick,\n  children = \"Follow\",\n  className,\n  ...props\n}: AuthorCardFollowProps) {\n  const { id, variant } = useAuthor(\"AuthorCardFollow\");\n  const [internal, setInternal] = useState(defaultPressed);\n  const current = pressed ?? internal;\n  const Icon = current ? Check : Plus;\n  return (\n    <Button\n      aria-describedby={`${id}-name`}\n      data-slot=\"author-card-follow\"\n      className={cn(\n        actionClass,\n        \"inline-flex cursor-pointer items-center justify-center gap-1.5 rounded-full border-0 bg-primary py-0 font-medium text-primary-foreground hover:bg-primary hover:brightness-108\",\n        \"aria-pressed:bg-secondary aria-pressed:text-secondary-foreground aria-pressed:hover:bg-[color-mix(in_oklab,var(--secondary)_85%,var(--foreground))] aria-pressed:hover:filter-none\",\n        variant === \"compact\"\n          ? \"h-6.5 pr-2.5 pl-2 text-[12px] has-[>svg]:pr-2.5 has-[>svg]:pl-2\"\n          : \"h-7 pr-3 pl-2.5 text-[12.5px] has-[>svg]:pr-3 has-[>svg]:pl-2.5\",\n        className,\n      )}\n      {...props}\n      type=\"button\"\n      aria-pressed={current}\n      onClick={(event) => {\n        onClick?.(event);\n        if (event.defaultPrevented) return;\n        if (pressed === undefined) setInternal(!current);\n        onPressedChange?.(!current);\n      }}\n    >\n      <Icon\n        key={current ? \"on\" : \"off\"}\n        size={14}\n        strokeWidth={2}\n        aria-hidden=\"true\"\n        className={cn(\n          \"size-3.5\",\n          current &&\n            \"animate-in fade-in-0 zoom-in-60 duration-220 ease-[cubic-bezier(0.16,1,0.3,1)] motion-reduce:animate-none\",\n        )}\n      />\n      {children}\n    </Button>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/uai/author-card.tsx"
    }
  ],
  "categories": [
    "content",
    "community"
  ],
  "type": "registry:ui"
}