{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "avatar",
  "title": "Avatar",
  "description": "Avatar from the Get IT Done design system.",
  "dependencies": [
    "@radix-ui/react-avatar@^1.3.1",
    "class-variance-authority@^0.7.0",
    "lucide-react@^0.379.0"
  ],
  "registryDependencies": [
    "https://zero-design.itdone.de/r/labels-context.json",
    "https://zero-design.itdone.de/r/utils.json",
    "https://zero-design.itdone.de/r/theme.json"
  ],
  "files": [
    {
      "path": "registry/ui/avatar.tsx",
      "content": "'use client';\n\nimport * as AvatarPrimitive from '@radix-ui/react-avatar';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { Camera, User } from 'lucide-react';\nimport * as React from 'react';\n\nimport { cn } from '@/lib/utils';\nimport { useLabels, usePreset } from '@/components/ui/labels-context';\n\n/**\n * Sizes match the Figma \"Avatar\" component set (Component Library file \"🚧 GetITDone-Basic\",\n * page \"↳ Avatars\" 18:1350, frame \"Avatar\" 19:1012): Size={xs,sm,md,lg,xl,2xl} = {24,32,40,48,\n * 56,64}px. Every step lands on Tailwind's default spacing scale, so no arbitrary bracket value\n * is needed: xs=h-6/w-6, sm=h-8/w-8, md=h-10/w-10 (the pre-existing default), lg=h-12/w-12,\n * xl=h-14/w-14, 2xl=h-16/w-16.\n */\nconst avatarVariants = cva(\n  [\n    // AVATAR-A11Y-03: the circular clip lives on an inner wrapper (see the `Avatar` component\n    // body below), not here — clipping this root would also clip AvatarBadge/AvatarOnlineIndicator,\n    // which are positioned against this element's *square* bounding box corner and rendered as its\n    // siblings, not its children. `rounded-full` (without `overflow-hidden`) is kept so the\n    // focus-visible ring below still reads as a circle; it does not, on its own, clip anything.\n    'relative inline-flex shrink-0 transition-colors',\n    // AVATAR-MAT-03: disabled must dim/block regardless of the `interactive` computation — mirrors\n    // Chip's identical base-array placement (chip.tsx) — since `disabled=true` always forces\n    // `interactive` to false and an `interactive:true`-gated class would never apply here.\n    'aria-disabled:pointer-events-none aria-disabled:opacity-disabled',\n    'data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-disabled',\n  ],\n  {\n    variants: {\n      size: {\n        // 16px for use inside tabs, chips and other dense inline slots (one step below Figma's xs).\n        '2xs': 'h-4 w-4',\n        xs: 'h-6 w-6',\n        sm: 'h-8 w-8',\n        md: 'h-10 w-10',\n        lg: 'h-12 w-12',\n        xl: 'h-14 w-14',\n        '2xl': 'h-16 w-16',\n        // Profile page picture, 192px. Pair with AvatarUpload for an edit affordance.\n        profile: 'h-48 w-48',\n      },\n      // Avatar is a static image/initials display by default (matching Figma's Default/Hover/\n      // Focused states, which only exist to support the common \"click an avatar to open a\n      // profile menu\" pattern). Interactive mode is opt-in — see `onClick` below — so a purely\n      // decorative avatar (e.g. inside a read-only comment list) never gets an unreachable focus\n      // ring or a false hover affordance (mirrors Chip's `interactive` gating).\n      //\n      // MAT-AVATAR-02 (closed): hover/active feedback is a real token-driven state layer, not a\n      // bare CSS filter. Unlike Chip/Alert — flat, single-color surfaces where a `hover:bg-*`\n      // token swap is visible on its own — Avatar's content (`AvatarImage` or `AvatarFallback`)\n      // is fully opaque and paints over the root's own background, so swapping the *root's*\n      // background token the way Chip does would be invisible whenever a photo or fallback is\n      // present. Instead, an `::after` overlay (MD3's literal \"state layer\" anatomy: a tonal\n      // layer drawn *on top of* content) uses the same adaptive `foreground-default` token\n      // already used for text elsewhere in this file — dark tint over light content in light\n      // mode, light tint over dark content in dark mode — at 0% (rest) / 10% (hover) / 20%\n      // (active) opacity, all standard Tailwind opacity steps (no arbitrary value). This\n      // uniformly darkens/lightens a photo, initials, or the placeholder glyph alike. The\n      // previously-invented `brightness-90` active step is dropped; `active:brightness-95` is\n      // kept only as the same secondary accent Chip/Alert already layer on top of their own\n      // active background-token change (chip.tsx, alert.tsx).\n      // `circle` (default) is fully round; `square` is the Attio-style \"squircle\" (30% radius),\n      // typically used for workspaces/companies as opposed to people.\n      shape: {\n        circle: 'rounded-full',\n        square: 'rounded-[30%]',\n      },\n      interactive: {\n        true: [\n          'cursor-pointer',\n          // AVATAR-A11Y-04 (interim mitigation): `ring-ring-default` does not yet ship a working\n          // dark-mode override in the *published* the design tokens package\n          // (the source token JSON has one, but dist/theme.css never emits it — a token\n          // build/publish gap owned by design-tokens-maintainer, tracked as a token gap below).\n          // Until that package is rebuilt and republished, this ring reuses the sibling\n          // `general-ring-default` token, which already carries a correct, verified dark-mode\n          // value (rgb(59 130 246)) in the published package. Revert to `ring-ring-default` once\n          // AVATAR-A11Y-04 is closed upstream.\n          'focus-visible:outline-none focus-visible:ring-focus focus-visible:ring-offset-focus focus-visible:ring-offset-background focus-visible:ring-ring-default',\n          \"after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:bg-background-inverse after:opacity-0 after:transition-opacity after:content-['']\",\n          'hover:after:opacity-10',\n          'active:after:opacity-20 active:brightness-95',\n        ],\n        false: '',\n      },\n    },\n    defaultVariants: {\n      size: 'md',\n      shape: 'circle',\n      interactive: false,\n    },\n  },\n);\n\nexport interface AvatarProps\n  extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>,\n    VariantProps<typeof avatarVariants> {\n  /**\n   * Dims the avatar and blocks pointer/keyboard interaction. Only meaningful together with\n   * `onClick` (e.g. a profile-menu trigger that's momentarily unavailable) — mirrors Chip's\n   * `disabled`: expressed via `aria-disabled`/`data-disabled` since Radix's `Avatar.Root`\n   * renders a `<span>`, not a native form control.\n   */\n  disabled?: boolean;\n}\n\n/**\n * Passing `onClick` opts a given Avatar into the interactive \"profile trigger\" pattern: a real\n * tab stop with Enter/Space activation, hover/active/focus-visible feedback, and an optional\n * `disabled` state. Without `onClick` the Avatar stays a plain, non-focusable image/initials\n * display (the common case).\n *\n * AVATAR-A11Y-02 — accessible name is the consumer's responsibility: `role=\"button\"` is only\n * ever reliably named by an `AvatarImage`'s `alt` text, a visible text `AvatarFallback` child\n * (e.g. initials), or an explicit `aria-label`/`aria-labelledby` passed to `Avatar` itself (already\n * available — `AvatarProps` extends Radix `Avatar.Root`'s native props). A `<AvatarFallback />`\n * placeholder glyph alone provides none of these (its icon is `aria-hidden`), so an interactive\n * Avatar composed with only the placeholder MUST be given an explicit `aria-label` describing the\n * action (e.g. `aria-label=\"Open profile menu\"`) — see the `InteractivePlaceholder` story and the\n * regression test in avatar.test.tsx. This is intentionally not auto-generated: Avatar cannot infer\n * a truthful label from a decorative glyph.\n *\n * KNOWN TOKEN GAP — focus treatment (AVATAR-MAT-01): Figma's Focused state (node 1198:100676)\n * renders a 4px tonal halo in `semantic/primary/100`, not the thin ring used elsewhere. That\n * scale token is a static PRIMITIVE (defined once in `figma-tokens/core.json`/`default_theme.json`,\n * no light/dark pair), so reusing it here would render an identical pale-violet halo in dark mode\n * — failing the \"keep light+dark\" requirement and WCAG 1.4.11 non-text contrast against a dark\n * surface. Until an adaptive \"avatar focus ring\" (or dark-aware `primary-100`) token exists,\n * interactive Avatar reuses the system's standard ring focus-visible treatment (consistent with\n * Chip, Tooltip's trigger wrapper, and Button) rather than fabricating the halo without a safe\n * dark-mode value.\n *\n * KNOWN TOKEN GAP — focus ring dark-mode value (AVATAR-A11Y-04, blocker, owner:\n * design-tokens-maintainer): the bare `ring.default` token has a correct dark-mode value in the\n * token JSON source (`figma-tokens/default_theme_dark.json`), but the currently *published*\n * the design tokens (`styles/theme.css`) package never emits a dark-mode override for\n * `--colors-ring-default` in `dist/theme.css` (light-mode value, blue.600, leaks into dark mode —\n * ~2.83:1 against a plausible dark secondary surface, under the 3:1 AA non-text-contrast floor).\n * The sibling `--colors-general-ring-default` token built correctly with both light and dark\n * values, so this Avatar's focus-visible ring reuses `general-ring-default` as an interim\n * mitigation until design-tokens-maintainer rebuilds/republishes the package with a working\n * `--colors-ring-default` dark override — re-point back to `ring-ring-default` once that ships\n * (verify `--colors-ring-default` reads `59 130 246` after `setMode('dark')`). Button, Chip, and\n * Tooltip's trigger wrapper reuse the same bare `ring-default` token and share this exposure but\n * are out of scope for this fix (see their own components).\n */\n// Size of the surrounding Avatar, so the initials pick their type role without a repeated `size` prop.\nconst AvatarSizeContext = React.createContext<NonNullable<VariantProps<typeof avatarVariants>['size']> | undefined>(undefined);\n\nconst Avatar = React.forwardRef<React.ElementRef<typeof AvatarPrimitive.Root>, AvatarProps>(\n  ({ className, size, shape, disabled, onClick, onKeyDown, children, ...props }, ref) => {\n    const interactive = Boolean(onClick) && !disabled;\n\n    const handleKeyDown = (event: React.KeyboardEvent<HTMLSpanElement>) => {\n      onKeyDown?.(event);\n      if (!interactive || event.defaultPrevented) return;\n      if (event.key === 'Enter' || event.key === ' ') {\n        event.preventDefault();\n        onClick?.(event as unknown as React.MouseEvent<HTMLSpanElement>);\n      }\n    };\n\n    // AVATAR-A11Y-03: AvatarBadge/AvatarOnlineIndicator are positioned against this root's\n    // *square* bounding-box corner, but the root's visible region is the inscribed circle — so\n    // clipping the root itself (the previous `overflow-hidden` here) clips almost all of the\n    // badge away. Split children so AvatarImage/AvatarFallback (and any other content) render\n    // inside a circularly-clipped inner wrapper, while the badge renders as an un-clipped sibling\n    // of that wrapper, still visually anchored to this root via `absolute bottom-0 right-0`.\n    const { content, badges } = React.Children.toArray(children).reduce<{\n      content: React.ReactNode[];\n      badges: React.ReactNode[];\n    }>(\n      (acc, child) => {\n        if (React.isValidElement(child) && (child.type === AvatarBadge || child.type === AvatarOnlineIndicator || child.type === AvatarUpload)) {\n          acc.badges.push(child);\n        } else {\n          acc.content.push(child);\n        }\n        return acc;\n      },\n      { content: [], badges: [] },\n    );\n\n    return (\n      <AvatarPrimitive.Root\n        ref={ref}\n        role={interactive ? 'button' : undefined}\n        tabIndex={interactive ? 0 : undefined}\n        aria-disabled={disabled || undefined}\n        data-disabled={disabled || undefined}\n        onClick={interactive ? onClick : undefined}\n        onKeyDown={handleKeyDown}\n        className={cn(avatarVariants({ size, shape, interactive }), className)}\n        {...props}\n      >\n        <span\n          className={cn(\n            'absolute inset-0 overflow-hidden border border-solid border-hairline',\n            shape === 'square' ? 'rounded-[30%]' : 'rounded-full',\n          )}\n        >\n          <AvatarSizeContext.Provider value={size ?? 'md'}>{content}</AvatarSizeContext.Provider>\n        </span>\n        {badges}\n      </AvatarPrimitive.Root>\n    );\n  },\n);\nAvatar.displayName = AvatarPrimitive.Root.displayName;\n\nconst AvatarImage = React.forwardRef<\n  React.ElementRef<typeof AvatarPrimitive.Image>,\n  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>\n>(({ className, ...props }, ref) => (\n  <AvatarPrimitive.Image ref={ref} className={cn('aspect-square h-full w-full object-cover', className)} {...props} />\n));\nAvatarImage.displayName = AvatarPrimitive.Image.displayName;\n\n/**\n * Covers both of Figma's non-image content variants under one slot, matching how they share\n * identical anatomy (a `background-muted` circle, centered content) and only differ in what's\n * centered inside:\n * - `Text=True` (initials, e.g. \"OR\") — pass the initials as `children`.\n * - `Placeholder=True, Text=False` (no data available) — omit `children` and a generic person\n *   glyph is rendered, matching Figma's placeholder icon.\n *\n * KNOWN TOKEN GAP (AVATAR-MAT-02): a dedicated `avatar-background-default` /\n * `avatar-foreground-default` token pair exists and is the closer literal match to Figma's\n * `semantic/gray/100` (bg) / `semantic/gray/600` (text) bindings — but the *published*\n * the design tokens (`styles/theme.css`) package has not rebuilt with the dark-mode override that\n * already exists in the token source (`figma-tokens/default_theme_dark.json` → `avatar.background.\n * default` = slate.700, `avatar.foreground.default` = slate.50), the same stale-package pattern\n * already flagged for Tooltip (see tooltip.figma.tsx). Until design-tokens-maintainer rebuilds and\n * republishes, this file intentionally keeps the pre-existing `background-muted` semantic token\n * (already dark-mode-safe) instead of switching to the not-yet-adaptive avatar-scoped background\n * token.\n *\n * MAT-AVATAR-01 / AVATAR-A11Y-01 (closed): the foreground half of that same pre-existing pair —\n * `text-text-subtle` — computed to only ~4.34:1 against `bg-background-neutral-default` in light mode\n * (slate.500 #64748b on slate.100 #f1f5f9), under the 4.5:1 AA minimum for 14px/500-weight text\n * (dark mode already passed at ~5.71:1). Swapped to `text-text-default`, which is verified\n * ≥13:1 against `bg-background-neutral-default` in BOTH modes (light: slate.900 on slate.100 ≈16.3:1; dark:\n * slate.50 on slate.800 ≈14:1) — restoring light/dark parity without introducing a new token.\n */\n// Initials colours (Figma Avatar Color=1..8 -> data/avatar/N default + on; Color=Brand; Color=Project 1..8 -> data/project/N + on-brand).\nconst avatarFallbackColors = {\n  brand: 'bg-background-brand-bold-default text-text-on-brand',\n  1: 'bg-data-avatar-1-default text-data-avatar-1-on',\n  2: 'bg-data-avatar-2-default text-data-avatar-2-on',\n  3: 'bg-data-avatar-3-default text-data-avatar-3-on',\n  4: 'bg-data-avatar-4-default text-data-avatar-4-on',\n  5: 'bg-data-avatar-5-default text-data-avatar-5-on',\n  6: 'bg-data-avatar-6-default text-data-avatar-6-on',\n  7: 'bg-data-avatar-7-default text-data-avatar-7-on',\n  8: 'bg-data-avatar-8-default text-data-avatar-8-on',\n  9: 'bg-data-avatar-9-default text-data-avatar-9-on',\n  10: 'bg-data-avatar-10-default text-data-avatar-10-on',\n  // Named aliases of slots 9 and 10 (Figma Avatar Color=Indigo, Color=Red).\n  indigo: 'bg-data-avatar-9-default text-data-avatar-9-on',\n  red: 'bg-data-avatar-10-default text-data-avatar-10-on',\n  'project-1': 'bg-data-project-1 text-data-project-1-on',\n  'project-2': 'bg-data-project-2 text-data-project-2-on',\n  'project-3': 'bg-data-project-3 text-data-project-3-on',\n  'project-4': 'bg-data-project-4 text-data-project-4-on',\n  'project-5': 'bg-data-project-5 text-data-project-5-on',\n  'project-6': 'bg-data-project-6 text-data-project-6-on',\n  'project-7': 'bg-data-project-7 text-data-project-7-on',\n  'project-8': 'bg-data-project-8 text-data-project-8-on',\n} as const;\n\ntype AvatarFallbackColor = keyof typeof avatarFallbackColors;\n\nconst hashName = (value: string) => Array.from(value).reduce((sum, char) => (sum * 31 + char.charCodeAt(0)) % 9973, 7);\n\nconst AvatarFallback = React.forwardRef<\n  React.ElementRef<typeof AvatarPrimitive.Fallback>,\n  Omit<React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>, 'color'> & {\n    /** Opt-in initials role: `xs` avatar-xs (10/600), `sm` ui-semibold (13/600), `profile` display (44/600) for the 192px profile avatar; omit for the default. */\n    size?: '2xs' | 'xs' | 'sm' | 'profile';\n    // Opt-in initials colour: `brand`, `1`..`10` (person), `indigo` (9), `red` (10) or `project-1`..`project-8`. Omit for the neutral default.\n    color?: AvatarFallbackColor;\n  }\n>(({ className, children, size: sizeProp, color: colorProp, ...props }, ref) => {\n  const zero = usePreset() === 'zero';\n  const parentSize = React.useContext(AvatarSizeContext);\n  // The Avatar size drives the initials role unless a fallback size is given. Sizes without an own role keep the default.\n  const inherited = parentSize === '2xs' || parentSize === 'xs' || parentSize === 'sm' || parentSize === 'profile' ? parentSize : undefined;\n  const size = sizeProp ?? inherited;\n  // Zero: initials get one of the eight person colours from the name, so the same person always has the same colour.\n  const color = colorProp ?? (zero && typeof children === 'string' && children.length > 0 ? ((hashName(children) % 8) + 1) as AvatarFallbackColor : undefined);\n  return (\n    <AvatarPrimitive.Fallback\n      ref={ref}\n      className={cn(\n        'flex h-full w-full items-center justify-center rounded-[inherit]',\n        color ? avatarFallbackColors[color] : 'bg-background-neutral-default text-text-default',\n        size === '2xs' || size === 'xs' ? 'typography-avatar-xs' : size === 'sm' ? 'typography-ui-semibold' : size === 'profile' ? 'typography-display' : 'text-sm font-medium',\n        className,\n      )}\n      {...props}\n    >\n      {children ?? <User className=\"h-3/5 w-3/5\" aria-hidden=\"true\" />}\n    </AvatarPrimitive.Fallback>\n  );\n});\nAvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;\n\n/**\n * Positioning + \"cutout\" primitive for the bottom-right status corner in Figma's \"Avatar\" set\n * (Status icon={Online indicator, Company, Verified}). The three Figma variants share this exact\n * anatomy (absolute bottom-right, a border matching whatever surface the Avatar sits on) but\n * diverge completely in payload — a solid color dot vs. an arbitrary company-logo `<img>` vs. a\n * borderless verified-check icon — so this stays a generic slot rather than a rigid `cva` enum\n * that would have to fabricate one true \"size\"/\"shape\" across three visually distinct badge\n * types. Consumers size and fill it (see `AvatarOnlineIndicator` below for the one fully\n * token-driven case); a company logo or verified icon is passed as `children`.\n *\n * `border-surface-canvas` (adaptive: white in light mode, near-black in dark mode) is used\n * for the cutout ring rather than Figma's literal `basic/white` binding — a literal white ring\n * would show as an unwanted halo against a dark-mode surface. This intentionally deviates from\n * the literal Figma value to satisfy the \"keep light+dark\" requirement; flag as NEEDS_DECISION\n * if a designer confirms the ring must always render pure white regardless of theme.\n *\n * AVATAR-A11Y-03 (closed): must be passed as a *direct* child of `Avatar` (as every story does),\n * not nested inside another element — `Avatar` type-checks its direct children against\n * `AvatarBadge`/`AvatarOnlineIndicator` to route the badge outside the circularly-clipped inner\n * content wrapper so this corner cutout is never clipped away (see `Avatar`'s body in this file).\n */\nconst AvatarBadge = React.forwardRef<HTMLSpanElement, React.HTMLAttributes<HTMLSpanElement>>(\n  ({ className, ...props }, ref) => (\n    <span\n      ref={ref}\n      className={cn(\n        'absolute bottom-0 right-0 flex items-center justify-center rounded-full border-2 border-solid border-surface-canvas',\n        className,\n      )}\n      {...props}\n    />\n  ),\n);\nAvatarBadge.displayName = 'AvatarBadge';\n\nexport interface AvatarUploadProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {\n  // Replaces the camera glyph.\n  icon?: React.ReactNode;\n  // Accessible name. Wins over the provider text.\n  uploadLabel?: string;\n}\n\n// Upload affordance in the bottom right corner of an Avatar (most useful with size \"profile\"). A real button; the parent opens the file picker. Pass it as a direct child of Avatar.\nconst AvatarUpload = React.forwardRef<HTMLButtonElement, AvatarUploadProps>(\n  ({ className, icon, uploadLabel, type = 'button', ...props }, ref) => {\n    const l = useLabels({ avatarUpload: uploadLabel });\n    return (\n      <button\n        ref={ref}\n        type={type}\n        aria-label={l.avatarUpload}\n        className={cn(\n          'absolute bottom-0 right-0 inline-flex h-control-md w-control-md cursor-pointer items-center justify-center rounded-full border-2 border-solid border-surface-canvas bg-background-inverse text-text-inverse transition-opacity',\n          'hover:opacity-90 active:opacity-80 disabled:cursor-not-allowed disabled:opacity-disabled',\n          '[&>svg]:h-icon-sm [&>svg]:w-icon-sm',\n          'focus-visible:outline-none focus-visible:ring-focus focus-visible:ring-offset-focus focus-visible:ring-offset-background focus-visible:ring-ring-default',\n          className,\n        )}\n        {...props}\n      >\n        {icon ?? <Camera aria-hidden=\"true\" />}\n      </button>\n    );\n  },\n);\nAvatarUpload.displayName = 'AvatarUpload';\n\n/**\n * The one status-corner variant Figma fully specifies with tokens alone (no external asset):\n * a solid dot, sized at a fixed 1/4 of the Avatar's own size at every step (24→6, 32→8, 40→10,\n * 48→12, 56→14, 64→16px — confirmed against Figma nodes 1034:556 (xs) and 1217:104703 (2xl)),\n * which lands on Tailwind's default spacing scale at every size. Color uses the adaptive\n * `general-success-default` role token (dark-mode-safe) rather than the static `success-500`\n * scale primitive Figma's binding names, since the latter has no dark-mode pair either — same\n * token-gap class as AVATAR-MAT-01/02 above.\n */\nconst avatarOnlineIndicatorSizes = {\n  '2xs': 'h-1 w-1',\n  xs: 'h-[6px] w-[6px]',\n  sm: 'h-2 w-2',\n  md: 'h-[10px] w-[10px]',\n  lg: 'h-3 w-3',\n  xl: 'h-[14px] w-[14px]',\n  '2xl': 'h-4 w-4',\n  profile: 'h-12 w-12',\n} as const;\n\nexport interface AvatarOnlineIndicatorProps extends React.HTMLAttributes<HTMLSpanElement> {\n  size?: keyof typeof avatarOnlineIndicatorSizes;\n  // Screen reader text. Wins over the provider text.\n  onlineLabel?: string;\n}\n\nconst AvatarOnlineIndicator = React.forwardRef<HTMLSpanElement, AvatarOnlineIndicatorProps>(\n  ({ className, size = 'md', onlineLabel, ...props }, ref) => {\n    const l = useLabels({ online: onlineLabel });\n    return (\n      <AvatarBadge\n        ref={ref}\n        className={cn('bg-general-success-default', avatarOnlineIndicatorSizes[size], className)}\n        {...props}\n      >\n        <span className=\"sr-only\">{l.online}</span>\n      </AvatarBadge>\n    );\n  },\n);\nAvatarOnlineIndicator.displayName = 'AvatarOnlineIndicator';\n\nexport interface AvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n  /** Size of the optional `+n` overflow avatar; match the grouped avatars. */\n  size?: NonNullable<VariantProps<typeof avatarVariants>['size']>;\n  /** Number of people not shown; renders a muted `+n` avatar at the end when greater than zero. */\n  overflowCount?: number;\n  // Screen reader suffix of the `+n` avatar. Wins over the provider text.\n  overflowLabel?: string;\n}\n\n// Overlapping avatars with a surface-coloured ring (size-avatar-ring). The -6px overlap has no token yet.\nconst AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>(\n  ({ className, size = 'xs', overflowCount = 0, overflowLabel, children, ...props }, ref) => {\n    const l = useLabels({ overflowMore: overflowLabel });\n    return (\n    <div\n      ref={ref}\n      role=\"group\"\n      className={cn(\n        'flex items-center',\n        '[&>*]:ring-[length:var(--size-avatar-ring)] [&>*]:ring-background [&>*+*]:-ml-[6px]',\n        className,\n      )}\n      {...props}\n    >\n      {children}\n      {overflowCount > 0 && (\n        <Avatar size={size}>\n          <AvatarFallback size={size === '2xs' ? '2xs' : size === 'xs' ? 'xs' : 'sm'} className=\"bg-muted text-text-muted\">\n            +{overflowCount}\n            <span className=\"sr-only\">{l.overflowMore}</span>\n          </AvatarFallback>\n        </Avatar>\n      )}\n    </div>\n    );\n  },\n);\nAvatarGroup.displayName = 'AvatarGroup';\n\nexport { Avatar, avatarFallbackColors, type AvatarFallbackColor, AvatarGroup, avatarVariants, AvatarBadge, AvatarFallback, AvatarImage, AvatarOnlineIndicator, AvatarUpload };\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}