{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "avatar",
  "type": "registry:ui",
  "dependencies": [
    "class-variance-authority",
    "@radix-ui/react-slot",
    "lucide-react"
  ],
  "registryDependencies": [
    "https://ui.getlyse.com/r/lyse-tokens.json"
  ],
  "files": [
    {
      "path": "registry/new-york/ui/avatar/avatar.tsx",
      "content": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Plus, User } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport \"./avatar.css\"\n\n/* ------------------------------------------------------------------ */\n/*  CVA                                                                */\n/* ------------------------------------------------------------------ */\n\nconst avatarVariants = cva(\n  \"avatar relative inline-flex items-center justify-center shrink-0 rounded-[var(--layout-radius-full)]\",\n  {\n    variants: {\n      size: {\n        xs: \"h-[var(--layout-size-md)] w-[var(--layout-size-md)]\",\n        sm: \"h-[var(--layout-size-lg)] w-[var(--layout-size-lg)]\",\n        md: \"h-[var(--layout-size-xl)] w-[var(--layout-size-xl)]\",\n        lg: \"h-[var(--layout-size-2xl)] w-[var(--layout-size-2xl)]\",\n        xl: \"h-[var(--layout-size-3xl)] w-[var(--layout-size-3xl)]\",\n      },\n    },\n    defaultVariants: {\n      size: \"md\",\n    },\n  }\n)\n\nconst avatarStatusVariants = cva(\n  \"avatar-status absolute bottom-0 right-0 rounded-[var(--layout-radius-full)]\",\n  {\n    variants: {\n      size: {\n        xs: \"size-1.5\",\n        sm: \"size-2\",\n        md: \"size-2.5\",\n        lg: \"size-3\",\n        xl: \"size-3.5\",\n      },\n    },\n    defaultVariants: {\n      size: \"md\",\n    },\n  }\n)\n\nconst avatarAddButtonVariants = cva(\n  \"avatar-add-button inline-flex items-center justify-center shrink-0 rounded-[var(--layout-radius-full)] p-[var(--layout-padding-xs)] cursor-pointer\",\n  {\n    variants: {\n      size: {\n        xs: \"h-[var(--layout-size-md)] w-[var(--layout-size-md)] [&_svg]:size-3\",\n        sm: \"h-[var(--layout-size-lg)] w-[var(--layout-size-lg)] [&_svg]:h-[var(--layout-size-xs)] [&_svg]:w-[var(--layout-size-xs)]\",\n        md: \"h-[var(--layout-size-xl)] w-[var(--layout-size-xl)] [&_svg]:h-[var(--layout-size-sm)] [&_svg]:w-[var(--layout-size-sm)]\",\n      },\n    },\n    defaultVariants: {\n      size: \"sm\",\n    },\n  }\n)\n\n/* ------------------------------------------------------------------ */\n/*  Avatar                                                             */\n/* ------------------------------------------------------------------ */\n\nfunction Avatar({\n  className,\n  size = \"md\",\n  src,\n  alt = \"\",\n  initials,\n  status,\n  companySrc,\n  children,\n  ref,\n  ...props\n}: React.ComponentProps<\"div\"> &\n  VariantProps<typeof avatarVariants> & {\n    src?: string\n    alt?: string\n    initials?: string\n    status?: \"online\" | \"offline\" | \"busy\" | \"company\"\n    companySrc?: string\n  }) {\n  const [imageError, setImageError] = React.useState(false)\n\n  React.useEffect(() => {\n    setImageError(false)\n  }, [src])\n\n  const displayInitials = initials?.slice(0, 2)\n  const showImage = src && !imageError\n\n  // When no image is visible, propagate alt as accessible name on the root\n  const ariaLabel = !showImage && alt ? alt : undefined\n\n  return (\n    <div\n      ref={ref}\n      data-slot=\"avatar\"\n      role={ariaLabel ? \"img\" : undefined}\n      aria-label={ariaLabel}\n      className={cn(avatarVariants({ size, className }))}\n      {...props}\n    >\n      <span className=\"avatar-content size-full inline-flex items-center justify-center overflow-hidden rounded-[var(--layout-radius-full)]\">\n        {showImage ? (\n          <img\n            src={src}\n            alt={alt}\n            onError={() => setImageError(true)}\n            className=\"size-full object-cover\"\n          />\n        ) : displayInitials ? (\n          <span className={cn(\n            \"avatar-initials font-accent select-none\",\n            size === \"xl\" ? \"text-content-body\" :\n            size === \"lg\" ? \"text-content-note\" :\n            \"text-content-caption\"\n          )}>\n            {displayInitials}\n          </span>\n        ) : (\n          <User className=\"avatar-placeholder\" aria-hidden=\"true\" />\n        )}\n      </span>\n\n      {status && (\n        <span\n          className={cn(\n            avatarStatusVariants({ size }),\n            status === \"company\" ? \"avatar-status-company\" : `avatar-status-${status}`\n          )}\n          aria-hidden=\"true\"\n        >\n          {status === \"company\" && companySrc && (\n            <img\n              src={companySrc}\n              alt=\"\"\n              className=\"size-full rounded-[var(--layout-radius-full)] object-cover\"\n            />\n          )}\n        </span>\n      )}\n    </div>\n  )\n}\n\n/* ------------------------------------------------------------------ */\n/*  AvatarLabel                                                        */\n/* ------------------------------------------------------------------ */\n\nconst avatarInLabelSizeMap = {\n  sm: \"sm\",\n  md: \"md\",\n  lg: \"lg\",\n} as const satisfies Record<string, \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\">\n\nfunction AvatarLabel({\n  className,\n  size = \"md\",\n  name,\n  description,\n  src,\n  alt,\n  initials,\n  status,\n  companySrc,\n  ref,\n  ...props\n}: Omit<React.ComponentProps<\"div\">, \"children\"> & {\n  size?: \"sm\" | \"md\" | \"lg\"\n  name: string\n  description?: string\n  src?: string\n  alt?: string\n  initials?: string\n  status?: \"online\" | \"offline\" | \"busy\" | \"company\"\n  companySrc?: string\n}) {\n  return (\n    <div\n      ref={ref}\n      data-slot=\"avatar-label\"\n      className={cn(\n        \"inline-flex items-center gap-[var(--layout-gap-md)]\",\n        className\n      )}\n      {...props}\n    >\n      <Avatar\n        size={avatarInLabelSizeMap[size ?? \"md\"]}\n        src={src}\n        alt={alt}\n        initials={initials}\n        status={status}\n        companySrc={companySrc}\n      />\n      <div className=\"flex flex-col min-w-0\">\n        <span\n          className={cn(\n            \"avatar-label-name font-accent truncate\",\n            size === \"lg\" ? \"text-content-body\" : \"text-content-note\"\n          )}\n        >\n          {name}\n        </span>\n        {description && (\n          <span className={cn(\"avatar-label-description truncate\", size === \"lg\" ? \"text-content-note\" : \"text-content-caption\")}>\n            {description}\n          </span>\n        )}\n      </div>\n    </div>\n  )\n}\n\n/* ------------------------------------------------------------------ */\n/*  AvatarGroup                                                        */\n/* ------------------------------------------------------------------ */\n\nconst avatarGroupVariants = cva(\"inline-flex items-center\", {\n  variants: {\n    size: {\n      xs: \"[&_[data-slot=avatar]]:h-[var(--layout-size-md)] [&_[data-slot=avatar]]:w-[var(--layout-size-md)] [&_[data-slot=avatar]]:-mr-2 [&_.avatar-group-overflow]:h-[var(--layout-size-md)] [&_.avatar-group-overflow]:w-[var(--layout-size-md)]\",\n      sm: \"[&_[data-slot=avatar]]:h-[var(--layout-size-lg)] [&_[data-slot=avatar]]:w-[var(--layout-size-lg)] [&_[data-slot=avatar]]:-mr-3 [&_.avatar-group-overflow]:h-[var(--layout-size-lg)] [&_.avatar-group-overflow]:w-[var(--layout-size-lg)]\",\n      md: \"[&_[data-slot=avatar]]:h-[var(--layout-size-xl)] [&_[data-slot=avatar]]:w-[var(--layout-size-xl)] [&_[data-slot=avatar]]:-mr-4 [&_.avatar-group-overflow]:h-[var(--layout-size-xl)] [&_.avatar-group-overflow]:w-[var(--layout-size-xl)]\",\n    },\n  },\n  defaultVariants: {\n    size: \"sm\",\n  },\n})\n\nfunction AvatarGroup({\n  className,\n  size = \"sm\",\n  max,\n  children,\n  ref,\n  ...props\n}: React.ComponentProps<\"div\"> &\n  VariantProps<typeof avatarGroupVariants> & {\n    max?: number\n  }) {\n  const childArray = React.Children.toArray(children)\n\n  const avatarChildren: React.ReactNode[] = []\n  const otherChildren: React.ReactNode[] = []\n\n  childArray.forEach((child) => {\n    if (React.isValidElement(child) && child.type === Avatar) {\n      avatarChildren.push(child)\n    } else {\n      otherChildren.push(child)\n    }\n  })\n\n  const visibleCount =\n    max !== undefined ? Math.min(Math.max(max, 0), avatarChildren.length) : avatarChildren.length\n  const overflowCount = avatarChildren.length - visibleCount\n  const visible = avatarChildren.slice(0, visibleCount)\n\n  return (\n    <div\n      ref={ref}\n      data-slot=\"avatar-group\"\n      className={cn(avatarGroupVariants({ size, className }))}\n      {...props}\n    >\n      <div className=\"inline-flex items-center\">\n        {visible.map((child, i) => (\n          <div\n            key={i}\n            className=\"avatar-group-item relative rounded-[var(--layout-radius-full)]\"\n            style={{ zIndex: visible.length - i }}\n          >\n            {child}\n          </div>\n        ))}\n        {overflowCount > 0 && (\n          <div\n            className=\"avatar-group-overflow avatar-group-item relative inline-flex items-center justify-center rounded-[var(--layout-radius-full)]\"\n            style={{ zIndex: 0 }}\n          >\n            <span className=\"text-content-caption font-accent select-none\">\n              +{overflowCount}\n            </span>\n          </div>\n        )}\n      </div>\n      {otherChildren.length > 0 && (\n        <div className=\"inline-flex items-center gap-[var(--layout-gap-sm)] ml-[var(--layout-gap-sm)]\">\n          {otherChildren}\n        </div>\n      )}\n    </div>\n  )\n}\n\n/* ------------------------------------------------------------------ */\n/*  AvatarAddButton                                                    */\n/* ------------------------------------------------------------------ */\n\nfunction AvatarAddButton({\n  className,\n  size = \"sm\",\n  disabled,\n  asChild,\n  children,\n  ref,\n  ...props\n}: React.ComponentProps<\"button\"> &\n  VariantProps<typeof avatarAddButtonVariants> & {\n    asChild?: boolean\n  }) {\n  const Comp = asChild ? Slot : \"button\"\n\n  return (\n    <Comp\n      ref={ref as React.Ref<HTMLButtonElement>}\n      data-slot=\"avatar-add-button\"\n      type={asChild ? undefined : \"button\"}\n      disabled={disabled}\n      aria-label=\"Add user\"\n      className={cn(avatarAddButtonVariants({ size, className }))}\n      {...props}\n    >\n      {asChild ? children : <Plus aria-hidden=\"true\" />}\n    </Comp>\n  )\n}\n\nexport {\n  Avatar,\n  AvatarLabel,\n  AvatarGroup,\n  AvatarAddButton,\n  avatarVariants,\n  avatarStatusVariants,\n  avatarAddButtonVariants,\n  avatarGroupVariants,\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/new-york/ui/avatar/avatar.css",
      "content": "/* ================================================================\n * AVATAR — Theming & State Styles\n * Structure (flex, size, radius) lives in avatar.tsx via cva.\n * Theming (colors, borders, states) lives here via tokens.\n * ================================================================ */\n\n/* --------------------------------\n * Avatar — fallback background (initials + placeholder)\n * -------------------------------- */\n.avatar {\n  background: var(--background-brand-faint-default);\n}\n\n.avatar-initials {\n  color: var(--text-brand-moderate);\n}\n\n.avatar-placeholder {\n  color: var(--text-brand-moderate);\n  width: 50%;\n  height: 50%;\n}\n\n/* --------------------------------\n * Status indicator — shared\n * -------------------------------- */\n.avatar-status {\n  border: var(--layout-border-thick) solid var(--border-base);\n}\n\n/* --------------------------------\n * Status indicator — variants\n * -------------------------------- */\n.avatar-status-online {\n  background: var(--background-success-strong-default);\n}\n\n.avatar-status-offline {\n  background: var(--background-neutral-bolder-default);\n}\n\n.avatar-status-busy {\n  background: var(--background-danger-strong-default);\n}\n\n.avatar-status-company {\n  border: var(--layout-border-thick) solid var(--border-base);\n  background: var(--background-base);\n  overflow: hidden;\n}\n\n/* --------------------------------\n * AvatarLabel — text\n * -------------------------------- */\n.avatar-label-name {\n  color: var(--text-base-strong);\n}\n\n.avatar-label-description {\n  color: var(--text-base-moderate);\n}\n\n/* --------------------------------\n * AvatarGroup — overlap ring\n * -------------------------------- */\n.avatar-group-item [data-slot=\"avatar\"] {\n  box-shadow: 0 0 0 var(--layout-border-thick) var(--border-base);\n}\n\n/* --------------------------------\n * AvatarGroup — overflow count\n * -------------------------------- */\n.avatar-group-overflow {\n  background: var(--background-brand-faint-default);\n  color: var(--text-brand-moderate);\n  box-shadow: 0 0 0 var(--layout-border-thick) var(--border-base);\n}\n\n/* --------------------------------\n * AvatarAddButton — base\n * -------------------------------- */\n.avatar-add-button {\n  background: var(--background-neutral-faint-default);\n  border: var(--layout-border-thin) solid var(--border-default);\n  color: var(--text-base-moderate);\n}\n\n@media (prefers-reduced-motion: no-preference) {\n  .avatar-add-button {\n    transition:\n      background 150ms ease,\n      border-color 150ms ease;\n  }\n}\n\n/* --------------------------------\n * AvatarAddButton — hover / pressed\n * -------------------------------- */\n.avatar-add-button:hover {\n  background: var(--background-neutral-faint-hover);\n}\n\n.avatar-add-button:active {\n  background: var(--background-neutral-faint-pressed);\n}\n\n/* --------------------------------\n * AvatarAddButton — focus ring\n * -------------------------------- */\n.avatar-add-button:focus-visible {\n  outline: none;\n  box-shadow: 0 0 0 2px var(--background-base), 0 0 0 4px var(--border-selected);\n}\n\n/* --------------------------------\n * AvatarAddButton — disabled\n * -------------------------------- */\n.avatar-add-button:disabled {\n  background: var(--background-disabled);\n  border-color: var(--border-disabled);\n  color: var(--text-disabled);\n  pointer-events: none;\n  cursor: default;\n}\n",
      "type": "registry:ui"
    }
  ]
}