{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "avatar",
  "type": "registry:ui",
  "registryDependencies": [],
  "dependencies": ["@base-ui/react", "@radix-ui/react-slot", "motion"],
  "devDependencies": [],
  "files": [
    {
      "path": "avatar.tsx",
      "content": "\"use client\";\n\nimport { Avatar as AvatarPrimitive } from \"@base-ui/react/avatar\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport * as React from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst componentThemeClassName =\n  \"[--ic-background:#ffffff] [--ic-foreground:#111111] [--ic-primary:#111111] [--ic-secondary:#646b75] [--ic-surface-border:#e9edf2] [--ic-border:#e3e7ec] [--ic-card:#ffffff] [--ic-card-foreground:#111111] [--ic-muted:#f5f7fa] [--ic-muted-foreground:#6d7480] [--ic-accent:#f3f5f8] [--color-accent:var(--ic-accent)] [--color-accent-foreground:var(--ic-accent-foreground)] [--ic-accent-foreground:#111111] [--ic-input:#e3e7ec] [--ic-ring:rgba(17,17,17,0.16)] [--ic-destructive:#dc2626] [--ic-paper:#fcfcfd] [--ic-popover-foreground:#111111] [--ic-brand:#0ea5e9] [--ic-brand-soft:#bae6fd] [--ic-shadow-soft:0_18px_38px_-24px_rgba(15,23,42,0.35)] [--ic-chart-1:oklch(0.52_0.19_254)] [--ic-chart-2:oklch(0.74_0.11_232)] [--ic-chart-3:oklch(0.42_0.16_262)] [--ic-chart-4:oklch(0.84_0.07_228)] [--ic-chart-5:oklch(0.62_0.14_240)] [--color-background:var(--ic-background)] [--color-foreground:var(--ic-foreground)] [--color-primary:var(--ic-primary)] [--color-secondary:var(--ic-secondary)] [--color-border:var(--ic-border)] [--color-card:var(--ic-card)] [--color-card-foreground:var(--ic-card-foreground)] [--color-muted:var(--ic-muted)] [--color-muted-foreground:var(--ic-muted-foreground)] [--color-accent:var(--ic-accent)] [--color-accent-foreground:var(--ic-accent-foreground)] [--color-input:var(--ic-input)] [--color-ring:var(--ic-ring)] [--color-destructive:var(--ic-destructive)] [--color-paper:var(--ic-paper)] [--color-popover-foreground:var(--ic-popover-foreground)] [--color-brand:var(--ic-brand)] [--color-brand-soft:var(--ic-brand-soft)] [--color-chart-1:var(--ic-chart-1)] [--color-chart-2:var(--ic-chart-2)] [--color-chart-3:var(--ic-chart-3)] [--color-chart-4:var(--ic-chart-4)] [--color-chart-5:var(--ic-chart-5)] dark:[--ic-background:#111111] dark:[--ic-foreground:#f6f3ec] dark:[--ic-primary:#f6f3ec] dark:[--ic-secondary:#cbc6bb] dark:[--ic-surface-border:#2a2a25] dark:[--ic-border:#2b2a25] dark:[--ic-card:#111111] dark:[--ic-card-foreground:#f6f3ec] dark:[--ic-muted:#171716] dark:[--ic-muted-foreground:#9a958a] dark:[--ic-accent:#1a1a18] [--color-accent:var(--ic-accent)] [--color-accent-foreground:var(--ic-accent-foreground)] dark:[--ic-accent-foreground:#f6f3ec] dark:[--ic-input:#2b2a25] dark:[--ic-ring:rgba(246,243,236,0.18)] dark:[--ic-destructive:#f87171] dark:[--ic-paper:#171716] dark:[--ic-popover-foreground:#f6f3ec] dark:[--ic-brand:#38bdf8] dark:[--ic-brand-soft:#0c4a6e] dark:[--ic-shadow-soft:0_20px_44px_-28px_rgba(0,0,0,0.6)] dark:[--ic-chart-1:oklch(0.68_0.17_250)] dark:[--ic-chart-2:oklch(0.82_0.09_225)] dark:[--ic-chart-3:oklch(0.58_0.15_260)] dark:[--ic-chart-4:oklch(0.75_0.12_235)] dark:[--ic-chart-5:oklch(0.88_0.06_220)]\";\n\ntype AvatarSize = \"default\" | \"sm\" | \"lg\";\ntype TooltipSide = \"top\" | \"bottom\" | \"left\" | \"right\";\ntype AvatarBadgeVariant = \"online\" | \"offline\" | \"busy\" | \"away\";\n\ntype TooltipTriggerProps = React.HTMLAttributes<HTMLElement> & {\n  \"aria-describedby\"?: string;\n  ref?: React.Ref<HTMLElement>;\n};\ntype TooltipTriggerElement = React.ReactElement<TooltipTriggerProps>;\n\nexport interface AvatarBadgeProps extends React.ComponentProps<\"span\"> {\n  tooltip?: string;\n  tooltipSide?: TooltipSide;\n  tooltipDelay?: number;\n  tooltipClassName?: string;\n  variant?: AvatarBadgeVariant;\n}\n\nexport interface AvatarProps extends AvatarPrimitive.Root.Props {\n  size?: AvatarSize;\n  name?: string;\n  asChild?: boolean;\n  tooltip?: string;\n  tooltipSide?: TooltipSide;\n  tooltipDelay?: number;\n  tooltipClassName?: string;\n}\n\nfunction hasFallbackChildren(children: React.ReactNode) {\n  if (children === undefined || children === null || children === false) {\n    return false;\n  }\n\n  if (typeof children === \"string\") {\n    return children.trim().length > 0;\n  }\n\n  if (Array.isArray(children)) {\n    return children.some(hasFallbackChildren);\n  }\n\n  return true;\n}\n\nconst MAX_TOOLTIP_CHARACTERS = 80;\nconst TOOLTIP_COLLISION_PADDING = 12;\nconst TOOLTIP_OFFSET = 10;\nconst DEFAULT_TOOLTIP_SIDE_ORDER: TooltipSide[] = [\n  \"right\",\n  \"left\",\n  \"top\",\n  \"bottom\",\n];\n\nconst WHITESPACE_REGEX = /\\s+/g;\nconst FALLBACK_SPLIT_REGEX = /[\\s_-]+/;\nconst NON_ALPHANUMERIC_REGEX = /[^\\p{L}\\p{N}]+/gu;\n\nconst AVATAR_IMAGE_SIZE: Record<AvatarSize, number> = {\n  sm: 24,\n  default: 32,\n  lg: 40,\n};\n\nconst BADGE_VARIANT_CLASSNAME: Record<AvatarBadgeVariant, string> = {\n  online: \"bg-emerald-500\",\n  offline: \"bg-muted-foreground\",\n  busy: \"bg-destructive\",\n  away: \"bg-amber-500\",\n};\n\nconst BADGE_VARIANT_LABEL: Record<AvatarBadgeVariant, string> = {\n  online: \"Online\",\n  offline: \"Offline\",\n  busy: \"Busy\",\n  away: \"Away\",\n};\n\ntype TooltipPlacement = {\n  left: number;\n  side: TooltipSide;\n  top: number;\n  transformOrigin: string;\n};\n\ntype AvatarContextValue = {\n  name?: string;\n  size: AvatarSize;\n};\n\nconst AvatarContext = React.createContext<AvatarContextValue>({\n  size: \"default\",\n});\n\nfunction useAvatarContext() {\n  return React.useContext(AvatarContext);\n}\n\nfunction normalizeText(value?: string) {\n  return value?.trim().replace(WHITESPACE_REGEX, \" \") ?? \"\";\n}\n\nfunction firstCharacter(value: string) {\n  return Array.from(value)[0] ?? \"\";\n}\n\nfunction getAvatarInitials(fallback?: string, name?: string, alt?: string) {\n  const normalized = [fallback, name, alt].map(normalizeText).find(Boolean);\n\n  if (!normalized) {\n    return \"?\";\n  }\n\n  const words = normalized\n    .split(FALLBACK_SPLIT_REGEX)\n    .map((word) => word.replace(NON_ALPHANUMERIC_REGEX, \"\"))\n    .filter(Boolean);\n\n  if (words.length >= 2) {\n    return `${firstCharacter(words[0])}${firstCharacter(words[1])}`.toUpperCase();\n  }\n\n  const compact = Array.from(\n    words[0] ?? normalized.replace(WHITESPACE_REGEX, \"\")\n  )\n    .slice(0, 2)\n    .join(\"\");\n\n  return compact.toUpperCase() || \"?\";\n}\n\nfunction mergeRefs<T>(...refs: Array<React.Ref<T> | undefined>) {\n  return (value: T | null) => {\n    for (const ref of refs) {\n      if (typeof ref === \"function\") {\n        ref(value);\n      } else if (ref) {\n        (ref as React.MutableRefObject<T | null>).current = value;\n      }\n    }\n  };\n}\n\nfunction isTooltipTriggerElement(\n  node: React.ReactNode\n): node is TooltipTriggerElement {\n  return React.isValidElement(node) && node.type !== React.Fragment;\n}\n\nfunction mergeDescribedBy(...ids: Array<string | undefined>) {\n  const merged = ids.filter(Boolean).join(\" \");\n\n  return merged.length > 0 ? merged : undefined;\n}\n\nfunction composeEventHandlers(\n  childHandler: React.EventHandler<React.SyntheticEvent> | undefined,\n  ownHandler: React.EventHandler<React.SyntheticEvent>\n) {\n  return (event: React.SyntheticEvent) => {\n    childHandler?.(event);\n\n    if (!event.defaultPrevented) {\n      ownHandler(event);\n    }\n  };\n}\n\nfunction clamp(value: number, min: number, max: number) {\n  return Math.min(Math.max(value, min), Math.max(min, max));\n}\n\nfunction getTooltipSideOrder(preferredSide?: TooltipSide) {\n  if (!preferredSide || preferredSide === \"right\") {\n    return DEFAULT_TOOLTIP_SIDE_ORDER;\n  }\n\n  return [\n    preferredSide,\n    ...DEFAULT_TOOLTIP_SIDE_ORDER.filter((side) => side !== preferredSide),\n  ];\n}\n\nfunction getTooltipTransformOrigin(side: TooltipSide) {\n  switch (side) {\n    case \"left\":\n      return \"right center\";\n    case \"top\":\n      return \"center bottom\";\n    case \"bottom\":\n      return \"center top\";\n    default:\n      return \"left center\";\n  }\n}\n\nfunction hasPrimaryAxisSpace(\n  side: TooltipSide,\n  triggerRect: DOMRect,\n  tooltipRect: DOMRect\n) {\n  switch (side) {\n    case \"left\":\n      return (\n        triggerRect.left - TOOLTIP_OFFSET - tooltipRect.width >=\n        TOOLTIP_COLLISION_PADDING\n      );\n    case \"top\":\n      return (\n        triggerRect.top - TOOLTIP_OFFSET - tooltipRect.height >=\n        TOOLTIP_COLLISION_PADDING\n      );\n    case \"bottom\":\n      return (\n        triggerRect.bottom + TOOLTIP_OFFSET + tooltipRect.height <=\n        window.innerHeight - TOOLTIP_COLLISION_PADDING\n      );\n    default:\n      return (\n        triggerRect.right + TOOLTIP_OFFSET + tooltipRect.width <=\n        window.innerWidth - TOOLTIP_COLLISION_PADDING\n      );\n  }\n}\n\nfunction getTooltipCandidate(\n  side: TooltipSide,\n  triggerRect: DOMRect,\n  tooltipRect: DOMRect\n): TooltipPlacement {\n  const maxLeft =\n    window.innerWidth - TOOLTIP_COLLISION_PADDING - tooltipRect.width;\n  const maxTop =\n    window.innerHeight - TOOLTIP_COLLISION_PADDING - tooltipRect.height;\n  const centeredLeft =\n    triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;\n  const centeredTop =\n    triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2;\n\n  switch (side) {\n    case \"left\":\n      return {\n        left: clamp(\n          triggerRect.left - TOOLTIP_OFFSET - tooltipRect.width,\n          TOOLTIP_COLLISION_PADDING,\n          maxLeft\n        ),\n        side,\n        top: clamp(centeredTop, TOOLTIP_COLLISION_PADDING, maxTop),\n        transformOrigin: getTooltipTransformOrigin(side),\n      };\n    case \"top\":\n      return {\n        left: clamp(centeredLeft, TOOLTIP_COLLISION_PADDING, maxLeft),\n        side,\n        top: clamp(\n          triggerRect.top - TOOLTIP_OFFSET - tooltipRect.height,\n          TOOLTIP_COLLISION_PADDING,\n          maxTop\n        ),\n        transformOrigin: getTooltipTransformOrigin(side),\n      };\n    case \"bottom\":\n      return {\n        left: clamp(centeredLeft, TOOLTIP_COLLISION_PADDING, maxLeft),\n        side,\n        top: clamp(\n          triggerRect.bottom + TOOLTIP_OFFSET,\n          TOOLTIP_COLLISION_PADDING,\n          maxTop\n        ),\n        transformOrigin: getTooltipTransformOrigin(side),\n      };\n    default:\n      return {\n        left: clamp(\n          triggerRect.right + TOOLTIP_OFFSET,\n          TOOLTIP_COLLISION_PADDING,\n          maxLeft\n        ),\n        side,\n        top: clamp(centeredTop, TOOLTIP_COLLISION_PADDING, maxTop),\n        transformOrigin: getTooltipTransformOrigin(side),\n      };\n  }\n}\n\nfunction getTooltipPlacement(\n  triggerRect: DOMRect,\n  tooltipRect: DOMRect,\n  preferredSide?: TooltipSide\n) {\n  const sideOrder = getTooltipSideOrder(preferredSide);\n  const resolvedSide =\n    sideOrder.find((side) =>\n      hasPrimaryAxisSpace(side, triggerRect, tooltipRect)\n    ) ??\n    sideOrder.at(-1) ??\n    \"bottom\";\n\n  return getTooltipCandidate(resolvedSide, triggerRect, tooltipRect);\n}\n\nconst tooltipBubbleClassName =\n  \"group/tooltip pointer-events-none fixed z-50 max-w-60 whitespace-normal rounded-lg bg-foreground px-3 py-1.5 font-medium text-background text-xs leading-snug shadow-[0_4px_24px_-4px_rgba(0,0,0,0.25)]\";\n\nconst tooltipArrowClassName =\n  \"absolute h-2 w-2 rotate-45 bg-foreground group-data-[side=bottom]/tooltip:-top-1 group-data-[side=left]/tooltip:top-1/2 group-data-[side=right]/tooltip:top-1/2 group-data-[side=left]/tooltip:-right-1 group-data-[side=top]/tooltip:-bottom-1 group-data-[side=bottom]/tooltip:left-1/2 group-data-[side=right]/tooltip:-left-1 group-data-[side=top]/tooltip:left-1/2 group-data-[side=bottom]/tooltip:-translate-x-1/2 group-data-[side=top]/tooltip:-translate-x-1/2 group-data-[side=left]/tooltip:-translate-y-1/2 group-data-[side=right]/tooltip:-translate-y-1/2\";\n\nfunction getTooltipMotionConfig(reduceMotion: boolean | null) {\n  const motionTransition = reduceMotion\n    ? { duration: 0 }\n    : {\n        type: \"spring\" as const,\n        stiffness: 400,\n        damping: 24,\n        mass: 0.6,\n      };\n\n  const arrowTransition = reduceMotion\n    ? { duration: 0 }\n    : {\n        type: \"spring\" as const,\n        stiffness: 500,\n        damping: 28,\n        delay: 0.03,\n      };\n\n  const visibleState = reduceMotion\n    ? { opacity: 1 }\n    : { opacity: 1, scale: 1, filter: \"blur(0px)\" };\n\n  const hiddenState = reduceMotion\n    ? { opacity: 0 }\n    : { opacity: 0, scale: 0.92, filter: \"blur(4px)\" };\n\n  return {\n    arrowTransition,\n    hiddenState,\n    motionTransition,\n    reduceMotion: Boolean(reduceMotion),\n    visibleState,\n  };\n}\n\nfunction useAvatarTooltip({\n  content,\n  delay,\n  side,\n}: {\n  content: string;\n  delay: number;\n  side: TooltipSide;\n}) {\n  const [open, setOpen] = React.useState(false);\n  const [placement, setPlacement] = React.useState<TooltipPlacement | null>(\n    null\n  );\n  const tooltipRef = React.useRef<HTMLDivElement | null>(null);\n  const triggerRef = React.useRef<HTMLElement | null>(null);\n  const timeoutRef = React.useRef<ReturnType<typeof setTimeout>>(undefined);\n  const tooltipId = React.useId();\n  const normalizedContent = content.trim();\n\n  React.useEffect(() => {\n    if (\n      process.env.NODE_ENV !== \"production\" &&\n      (normalizedContent.length > MAX_TOOLTIP_CHARACTERS ||\n        normalizedContent.includes(\"\\n\"))\n    ) {\n      console.warn(\n        \"Avatar tooltip content should stay short, single-line, and non-interactive. Use Tooltip directly for richer content.\"\n      );\n    }\n  }, [normalizedContent]);\n\n  const updatePlacement = React.useCallback(() => {\n    const trigger = triggerRef.current;\n    const tooltip = tooltipRef.current;\n\n    if (!(trigger && tooltip)) {\n      return;\n    }\n\n    setPlacement(\n      getTooltipPlacement(\n        trigger.getBoundingClientRect(),\n        tooltip.getBoundingClientRect(),\n        side\n      )\n    );\n  }, [side]);\n\n  const handleEnter = React.useCallback(\n    (event: React.SyntheticEvent) => {\n      triggerRef.current = event.currentTarget as HTMLElement;\n      setPlacement(null);\n      clearTimeout(timeoutRef.current);\n      timeoutRef.current = setTimeout(() => setOpen(true), delay * 1000);\n    },\n    [delay]\n  );\n\n  const handleLeave = React.useCallback(() => {\n    clearTimeout(timeoutRef.current);\n    setOpen(false);\n  }, []);\n\n  React.useEffect(() => () => clearTimeout(timeoutRef.current), []);\n\n  React.useEffect(() => {\n    if (!open) {\n      return;\n    }\n\n    const handleKeyDown = (event: KeyboardEvent) => {\n      if (event.key === \"Escape\") {\n        handleLeave();\n      }\n    };\n\n    window.addEventListener(\"keydown\", handleKeyDown);\n\n    return () => {\n      window.removeEventListener(\"keydown\", handleKeyDown);\n    };\n  }, [handleLeave, open]);\n\n  React.useLayoutEffect(() => {\n    if (!open) {\n      return;\n    }\n\n    updatePlacement();\n    const frameId = window.requestAnimationFrame(updatePlacement);\n\n    window.addEventListener(\"resize\", updatePlacement);\n    window.addEventListener(\"scroll\", updatePlacement, true);\n\n    return () => {\n      window.cancelAnimationFrame(frameId);\n      window.removeEventListener(\"resize\", updatePlacement);\n      window.removeEventListener(\"scroll\", updatePlacement, true);\n    };\n  }, [open, updatePlacement]);\n\n  return {\n    handleEnter,\n    handleLeave,\n    normalizedContent,\n    open,\n    placement,\n    tooltipId,\n    tooltipRef,\n    triggerRef,\n  };\n}\n\nfunction attachTooltipTrigger(\n  children: TooltipTriggerElement,\n  {\n    handleEnter,\n    handleLeave,\n    open,\n    tooltipId,\n    triggerRef,\n  }: {\n    handleEnter: (event: React.SyntheticEvent) => void;\n    handleLeave: () => void;\n    open: boolean;\n    tooltipId: string;\n    triggerRef: React.MutableRefObject<HTMLElement | null>;\n  }\n) {\n  const childProps = children.props;\n  const childAriaDescribedBy = childProps[\"aria-describedby\"];\n  const triggerDescription = open\n    ? mergeDescribedBy(childAriaDescribedBy, tooltipId)\n    : childAriaDescribedBy;\n\n  return React.cloneElement(children, {\n    \"aria-describedby\": triggerDescription,\n    onBlur: composeEventHandlers(childProps.onBlur, handleLeave),\n    onFocus: composeEventHandlers(childProps.onFocus, handleEnter),\n    onMouseEnter: composeEventHandlers(childProps.onMouseEnter, handleEnter),\n    onMouseLeave: composeEventHandlers(childProps.onMouseLeave, handleLeave),\n    onPointerEnter: composeEventHandlers(\n      childProps.onPointerEnter,\n      handleEnter\n    ),\n    onPointerLeave: composeEventHandlers(\n      childProps.onPointerLeave,\n      handleLeave\n    ),\n    ref: mergeRefs(childProps.ref, (node: HTMLElement | null) => {\n      triggerRef.current = node;\n    }),\n  });\n}\n\nfunction AvatarTooltipBubble({\n  className,\n  content,\n  motionConfig,\n  open,\n  placement,\n  side,\n  tooltipId,\n  tooltipRef,\n}: {\n  className?: string;\n  content: string;\n  motionConfig: ReturnType<typeof getTooltipMotionConfig>;\n  open: boolean;\n  placement: TooltipPlacement | null;\n  side: TooltipSide;\n  tooltipId: string;\n  tooltipRef: React.RefObject<HTMLDivElement | null>;\n}) {\n  if (typeof document === \"undefined\") {\n    return null;\n  }\n\n  const reduceMotion = motionConfig.reduceMotion;\n\n  return createPortal(\n    <AnimatePresence>\n      {open ? (\n        <motion.div\n          animate={motionConfig.visibleState}\n          className={cn(\n            componentThemeClassName,\n            tooltipBubbleClassName,\n            className\n          )}\n          data-side={placement?.side ?? side}\n          exit={motionConfig.hiddenState}\n          id={tooltipId}\n          initial={motionConfig.hiddenState}\n          ref={tooltipRef}\n          role=\"tooltip\"\n          style={{\n            left: placement?.left ?? 0,\n            top: placement?.top ?? 0,\n            transformOrigin:\n              placement?.transformOrigin ?? getTooltipTransformOrigin(side),\n            visibility: placement ? undefined : \"hidden\",\n          }}\n          transition={motionConfig.motionTransition}\n        >\n          <motion.span\n            animate={reduceMotion ? undefined : { scale: 1 }}\n            className={tooltipArrowClassName}\n            exit={reduceMotion ? undefined : { scale: 0.95, opacity: 0 }}\n            initial={reduceMotion ? undefined : { scale: 0.95, opacity: 0 }}\n            transition={motionConfig.arrowTransition}\n          />\n          {content}\n        </motion.div>\n      ) : null}\n    </AnimatePresence>,\n    document.body\n  );\n}\n\nfunction AvatarTooltipImpl({\n  children,\n  className,\n  content,\n  delay = 0.15,\n  side = \"right\",\n}: {\n  children: TooltipTriggerElement;\n  className?: string;\n  content: string;\n  delay?: number;\n  side?: TooltipSide;\n}) {\n  const reduceMotion = useReducedMotion();\n  const motionConfig = getTooltipMotionConfig(reduceMotion);\n  const {\n    handleEnter,\n    handleLeave,\n    normalizedContent,\n    open,\n    placement,\n    tooltipId,\n    tooltipRef,\n    triggerRef,\n  } = useAvatarTooltip({ content, delay, side });\n\n  if (normalizedContent.length === 0) {\n    return children;\n  }\n\n  return (\n    <>\n      {attachTooltipTrigger(children, {\n        handleEnter,\n        handleLeave,\n        open,\n        tooltipId,\n        triggerRef,\n      })}\n      <AvatarTooltipBubble\n        className={className}\n        content={normalizedContent}\n        motionConfig={motionConfig}\n        open={open}\n        placement={placement}\n        side={side}\n        tooltipId={tooltipId}\n        tooltipRef={tooltipRef}\n      />\n    </>\n  );\n}\n\nfunction AvatarTooltip({\n  children,\n  className,\n  content,\n  delay = 0.15,\n  side = \"right\",\n}: {\n  children: React.ReactNode;\n  className?: string;\n  content: string;\n  delay?: number;\n  side?: TooltipSide;\n}) {\n  if (!isTooltipTriggerElement(children)) {\n    if (process.env.NODE_ENV !== \"production\") {\n      console.error(\n        \"Avatar tooltip expects a single element child so it can forward hover, focus, and accessibility props.\"\n      );\n    }\n\n    return children;\n  }\n\n  return (\n    <AvatarTooltipImpl\n      className={className}\n      content={content}\n      delay={delay}\n      side={side}\n    >\n      {children}\n    </AvatarTooltipImpl>\n  );\n}\n\nconst Avatar = React.forwardRef<HTMLSpanElement, AvatarProps>(\n  (\n    {\n      \"aria-label\": ariaLabel,\n      asChild = false,\n      className,\n      name,\n      render,\n      size = \"default\",\n      tabIndex,\n      tooltip,\n      tooltipClassName,\n      tooltipDelay,\n      tooltipSide,\n      ...props\n    },\n    ref\n  ) => {\n    const normalizedTooltip = tooltip?.trim() ?? \"\";\n    const accessibleLabel = ariaLabel ?? (normalizedTooltip || undefined);\n    const avatarRoot = (\n      <AvatarPrimitive.Root\n        {...props}\n        aria-label={accessibleLabel}\n        className={cn(\n          \"group/avatar relative flex size-8 shrink-0 select-none rounded-full after:pointer-events-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten\",\n          className\n        )}\n        data-size={size}\n        data-slot=\"avatar\"\n        ref={ref}\n        render={render ?? (asChild ? <Slot /> : undefined)}\n        tabIndex={tabIndex ?? (normalizedTooltip ? 0 : undefined)}\n      />\n    );\n\n    return (\n      <AvatarContext.Provider value={{ name, size }}>\n        {normalizedTooltip ? (\n          <AvatarTooltip\n            className={tooltipClassName}\n            content={normalizedTooltip}\n            delay={tooltipDelay}\n            side={tooltipSide}\n          >\n            {avatarRoot}\n          </AvatarTooltip>\n        ) : (\n          avatarRoot\n        )}\n      </AvatarContext.Provider>\n    );\n  }\n);\nAvatar.displayName = \"Avatar\";\n\nconst AvatarImage = React.forwardRef<\n  HTMLImageElement,\n  AvatarPrimitive.Image.Props\n>(({ className, height, width, ...props }, ref) => {\n  const { size } = useAvatarContext();\n  const dimension = AVATAR_IMAGE_SIZE[size];\n\n  return (\n    <AvatarPrimitive.Image\n      className={cn(\n        \"aspect-square size-full overflow-hidden rounded-full object-cover\",\n        className\n      )}\n      data-slot=\"avatar-image\"\n      height={height ?? dimension}\n      ref={ref}\n      width={width ?? dimension}\n      {...props}\n    />\n  );\n});\nAvatarImage.displayName = \"AvatarImage\";\n\nconst AvatarFallback = React.forwardRef<\n  HTMLSpanElement,\n  AvatarPrimitive.Fallback.Props\n>(({ children, className, ...props }, ref) => {\n  const { name } = useAvatarContext();\n  const fallbackContent = hasFallbackChildren(children)\n    ? children\n    : getAvatarInitials(undefined, name);\n\n  return (\n    <AvatarPrimitive.Fallback\n      className={cn(\n        \"flex size-full items-center justify-center overflow-hidden rounded-full bg-muted text-muted-foreground text-sm group-data-[size=lg]/avatar:text-base group-data-[size=sm]/avatar:text-xs\",\n        className\n      )}\n      data-slot=\"avatar-fallback\"\n      ref={ref}\n      {...props}\n    >\n      {fallbackContent}\n    </AvatarPrimitive.Fallback>\n  );\n});\nAvatarFallback.displayName = \"AvatarFallback\";\n\nconst AvatarBadge = React.forwardRef<HTMLSpanElement, AvatarBadgeProps>(\n  (\n    {\n      \"aria-label\": ariaLabel,\n      className,\n      tabIndex,\n      tooltip,\n      tooltipClassName,\n      tooltipDelay,\n      tooltipSide,\n      variant = \"online\",\n      ...props\n    },\n    ref\n  ) => {\n    const normalizedTooltip = tooltip?.trim() ?? \"\";\n    const badgeLabel =\n      ariaLabel ?? (normalizedTooltip || BADGE_VARIANT_LABEL[variant]);\n    const badge = (\n      <span\n        aria-label={badgeLabel}\n        className={cn(\n          \"absolute right-0 bottom-0 z-10 inline-flex size-2.5 shrink-0 select-none items-center justify-center rounded-full ring-2 ring-background group-data-[size=default]/avatar:size-2.5 group-data-[size=lg]/avatar:size-3 group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden group-data-[size=default]/avatar:[&>svg]:size-2 group-data-[size=lg]/avatar:[&>svg]:size-2\",\n          BADGE_VARIANT_CLASSNAME[variant],\n          className\n        )}\n        data-slot=\"avatar-badge\"\n        role=\"img\"\n        tabIndex={tabIndex ?? (normalizedTooltip ? 0 : undefined)}\n        {...props}\n        ref={ref}\n      />\n    );\n\n    if (!normalizedTooltip) {\n      return badge;\n    }\n\n    return (\n      <AvatarTooltip\n        className={tooltipClassName}\n        content={normalizedTooltip}\n        delay={tooltipDelay}\n        side={tooltipSide}\n      >\n        {badge}\n      </AvatarTooltip>\n    );\n  }\n);\nAvatarBadge.displayName = \"AvatarBadge\";\n\nconst AvatarGroup = React.forwardRef<\n  HTMLDivElement,\n  React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => {\n  return (\n    <div\n      className={cn(\n        \"group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background *:data-[slot=avatar]:transition-[z-index] *:data-[slot=avatar-group-count]:hover:z-10 *:data-[slot=avatar]:hover:z-10 *:data-[slot=avatar-group-count]:focus-within:z-10 *:data-[slot=avatar]:focus-within:z-10\",\n        className\n      )}\n      data-slot=\"avatar-group\"\n      ref={ref}\n      {...props}\n    />\n  );\n});\nAvatarGroup.displayName = \"AvatarGroup\";\n\nconst AvatarGroupCount = React.forwardRef<\n  HTMLDivElement,\n  React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => {\n  return (\n    <div\n      className={cn(\n        \"relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground text-sm ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 group-has-data-[size=lg]/avatar-group:text-base group-has-data-[size=sm]/avatar-group:text-xs [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3\",\n        className\n      )}\n      data-slot=\"avatar-group-count\"\n      ref={ref}\n      {...props}\n    />\n  );\n});\nAvatarGroupCount.displayName = \"AvatarGroupCount\";\n\nexport {\n  Avatar,\n  AvatarBadge,\n  AvatarFallback,\n  AvatarGroup,\n  AvatarGroupCount,\n  AvatarImage,\n  getAvatarInitials,\n  type AvatarBadgeVariant,\n  type AvatarSize,\n};\n",
      "type": "registry:ui"
    }
  ],
  "title": "Avatar",
  "description": "Compound Base UI avatar with image, fallback, variant status badge, grouped stack, overflow count, auto initials, and tooltip-enabled status hints."
}
