{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "avatar-stack",
  "title": "Avatar Stack",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "An accessible compact row of people with initials, image support, and an overflow count.",
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/avatar-stack.tsx",
      "content": "// Ballmac UI: Avatar Stack. https://ui.ballmac.com/components/avatar-stack\nimport * as React from \"react\"\nimport { cn } from \"@/lib/utils\"\n\ntype AvatarPerson = {\n  /** Display name for screen readers and tooltip. */ name: string\n  /** Optional image URL. */ src?: string\n  /** Initials when no image is present. */ initials?: string\n}\ntype AvatarStackProps = React.ComponentProps<\"div\"> & {\n  /** People to display, in order. */\n  people: AvatarPerson[]\n  /** Maximum visible avatars before the overflow count. */\n  max?: number\n  /** Avatar diameter. */\n  size?: \"sm\" | \"default\"\n}\nfunction AvatarStack({\n  className,\n  people,\n  max = 4,\n  size = \"default\",\n  ...props\n}: AvatarStackProps) {\n  const visible = people.slice(0, Math.max(0, max))\n  const overflow = people.length - visible.length\n  return (\n    <div\n      data-slot=\"avatar-stack\"\n      data-size={size}\n      role=\"group\"\n      aria-label={people.map((person) => person.name).join(\", \")}\n      className={cn(\n        \"group/avatar-stack flex min-w-0 items-center -space-x-2\",\n        className,\n      )}\n      {...props}\n    >\n      {visible.map((person, index) => (\n        <span\n          key={`${person.name}-${index}`}\n          data-slot=\"avatar-stack-person\"\n          title={person.name}\n          aria-hidden=\"true\"\n          className=\"bg-muted text-muted-foreground border-background relative flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-full border-2 text-xs font-semibold data-[size=sm]:size-7 group-data-[size=sm]/avatar-stack:size-7\"\n        >\n          <span>\n            {person.initials ??\n              person.name\n                .split(/\\s+/)\n                .map((part) => part[0])\n                .slice(0, 2)\n                .join(\"\")}\n          </span>\n          {person.src && (\n            <img\n              src={person.src}\n              alt=\"\"\n              className=\"absolute inset-0 size-full object-cover\"\n            />\n          )}\n        </span>\n      ))}\n      {overflow > 0 && (\n        <span\n          data-slot=\"avatar-stack-overflow\"\n          aria-hidden=\"true\"\n          className=\"bg-secondary text-secondary-foreground border-background relative flex size-9 shrink-0 items-center justify-center rounded-full border-2 text-xs font-semibold group-data-[size=sm]/avatar-stack:size-7\"\n        >\n          +{overflow}\n        </span>\n      )}\n    </div>\n  )\n}\nexport { AvatarStack, type AvatarStackProps, type AvatarPerson }\n",
      "type": "registry:ui",
      "target": "@components/ballmac/avatar-stack.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "people",
      "avatar",
      "team"
    ],
    "version": "1.0.0",
    "updated": "2026-09-30",
    "ai": {
      "summary": "An accessible compact row of people with initials, image support, and an overflow count.",
      "whenToUse": [
        "Show participants or collaborators in limited space"
      ],
      "whenNotToUse": [
        "Use Avatar for one person"
      ],
      "composesWith": [
        "avatar"
      ],
      "a11y": [
        {
          "keys": "None",
          "action": "Static content is announced with semantic structure"
        }
      ],
      "customization": [
        "Content, layout, and token-based className styling"
      ]
    },
    "examples": [
      "avatar-stack-demo",
      "avatar-stack-states"
    ],
    "url": "https://ui.ballmac.com/components/avatar-stack"
  },
  "categories": [
    "data-display"
  ],
  "type": "registry:ui"
}