{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "avatar",
  "title": "Avatar",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-vue/components/avatar/Avatar.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { computed } from 'vue'\nimport { cn } from '@/lib/utils'\nimport { avatarVariants } from './avatar.variants'\n\n// Inlined unions: SFC compiler can't extract runtime props from\n// `AvatarVariants['size']` etc. indexed-access types.\nconst props = withDefaults(\n  defineProps<{\n    class?: HTMLAttributes['class']\n    size?: 'xs' | 'sm' | 'default' | 'lg' | 'xl' | '2xl'\n    rounded?: 'none' | 'sm' | 'default' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n    color?: 'default' | 'primary' | 'secondary' | 'destructive' | 'success' | 'warning' | 'info' | 'error' | 'muted'\n    variant?: 'default' | 'outlined' | 'soft'\n    tile?: boolean\n    disabled?: boolean\n    loading?: boolean\n  }>(),\n  {\n    tile: false,\n    disabled: false,\n    loading: false,\n  },\n)\n\nconst emit = defineEmits<{\n  click: [event: MouseEvent]\n  error: [event: Event]\n  load: [event: Event]\n}>()\n\nfunction handleClick(event: MouseEvent) {\n  if (!props.disabled) {\n    emit('click', event)\n  }\n}\n\nfunction handleError(event: Event) {\n  emit('error', event)\n}\n\nfunction handleLoad(event: Event) {\n  emit('load', event)\n}\n\nconst rootClasses = computed(() =>\n  cn(\n    avatarVariants({ size: props.size, rounded: props.rounded, color: props.color, variant: props.variant }),\n    props.tile ? 'rounded-none' : '',\n    props.disabled ? 'opacity-50 cursor-not-allowed' : '',\n    props.loading ? 'animate-pulse' : '',\n    props.class,\n  ),\n)\n</script>\n\n<template>\n  <span :class=\"rootClasses\" data-uipkge data-slot=\"avatar\" @click=\"handleClick\">\n    <slot />\n  </span>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/avatar/Avatar.vue"
    },
    {
      "path": "packages/registry-vue/components/avatar/AvatarFallback.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { computed } from 'vue'\nimport { cn } from '@/lib/utils'\nimport { avatarFallbackVariants } from './avatar.variants'\n\n// Inlined unions: SFC compiler can't extract runtime props from\n// indexed-access types.\nconst props = withDefaults(\n  defineProps<{\n    class?: HTMLAttributes['class']\n    size?: 'xs' | 'sm' | 'default' | 'lg' | 'xl' | '2xl'\n    color?: 'default' | 'primary' | 'secondary' | 'destructive' | 'success' | 'warning' | 'info' | 'error' | 'muted'\n    text?: string\n  }>(),\n  {\n    size: 'default',\n    color: 'default',\n  },\n)\n\nconst emit = defineEmits<{\n  click: [event: MouseEvent]\n}>()\n\nfunction handleClick(event: MouseEvent) {\n  emit('click', event)\n}\n\nconst rootClasses = computed(() => cn(avatarFallbackVariants({ size: props.size, color: props.color }), props.class))\n</script>\n\n<template>\n  <span :class=\"rootClasses\" data-uipkge data-slot=\"avatar-fallback\" @click=\"handleClick\">\n    <template v-if=\"text\">{{ text }}</template>\n    <slot v-else />\n  </span>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/avatar/AvatarFallback.vue"
    },
    {
      "path": "packages/registry-vue/components/avatar/AvatarGroup.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\nexport interface AvatarGroupProps {\n  class?: HTMLAttributes['class']\n  max?: number\n  overlap?: boolean\n  size?: 'xs' | 'sm' | 'default' | 'lg' | 'xl' | '2xl'\n  total?: number\n}\n\nconst props = withDefaults(defineProps<AvatarGroupProps>(), {\n  overlap: true,\n  size: 'default',\n})\n\nconst emit = defineEmits<{\n  click: [event: MouseEvent]\n}>()\n\nfunction handleClick(event: MouseEvent) {\n  emit('click', event)\n}\n</script>\n\n<template>\n  <div\n    :class=\"cn('flex items-center', overlap ? '-space-x-2' : 'gap-1', props.class)\"\n    data-uipkge\n    data-slot=\"avatar-group\"\n    @click=\"handleClick\"\n  >\n    <slot :max=\"max\" :size=\"size\" />\n    <template v-if=\"max && ($slots.default?.()?.length || 0) > max\">\n      <div\n        :class=\"\n          cn(\n            'bg-muted ring-background relative flex shrink-0 overflow-hidden rounded-full ring-2',\n            size === 'xs'\n              ? 'size-4 text-[8px]'\n              : size === 'sm'\n                ? 'size-6 text-xs'\n                : size === 'default'\n                  ? 'size-8 text-sm'\n                  : size === 'lg'\n                    ? 'size-12 text-base'\n                    : size === 'xl'\n                      ? 'size-16 text-lg'\n                      : 'size-20 text-xl',\n          )\n        \"\n      >\n        <slot name=\"overflow\" :count=\"($slots.default?.()?.length || 0) - max + 1\" />\n        <span v-if=\"!$slots['overflow']\" class=\"flex size-full items-center justify-center font-medium\">\n          +{{ ($slots.default?.()?.length || 0) - max + 1 }}\n        </span>\n      </div>\n    </template>\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/avatar/AvatarGroup.vue"
    },
    {
      "path": "packages/registry-vue/components/avatar/AvatarImage.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { ref, watch } from 'vue'\nimport { cn } from '@/lib/utils'\n\nconst props = withDefaults(\n  defineProps<{\n    class?: HTMLAttributes['class']\n    src?: string\n    alt?: string\n    fallback?: string\n    loading?: 'eager' | 'lazy'\n    referrerpolicy?:\n      | 'no-referrer'\n      | 'no-referrer-when-downgrade'\n      | 'origin'\n      | 'origin-when-cross-origin'\n      | 'same-origin'\n      | 'strict-origin'\n      | 'strict-origin-when-cross-origin'\n      | 'unsafe-url'\n      | ''\n    crossorigin?: 'anonymous' | 'use-credentials'\n    onerror?: (event: Event) => void\n    onload?: (event: Event) => void\n  }>(),\n  {\n    loading: 'lazy',\n  },\n)\n\nconst emit = defineEmits<{\n  error: [event: Event]\n  load: [event: Event]\n}>()\n\n// Track <img> failures so a broken URL falls back to the slot\n// (consumer renders AvatarFallback there). Without this, an HTTP 404\n// leaves the broken-image glyph forever, which contradicts the docs\n// promise that AvatarFallback shows when the image fails.\nconst errored = ref(false)\nwatch(\n  () => props.src,\n  () => {\n    errored.value = false\n  },\n)\n\nfunction handleError(event: Event) {\n  errored.value = true\n  emit('error', event)\n  if (props.onerror) {\n    props.onerror(event)\n  }\n}\n\nfunction handleLoad(event: Event) {\n  emit('load', event)\n  if (props.onload) {\n    props.onload(event)\n  }\n}\n</script>\n\n<template>\n  <img\n    v-if=\"src && !errored\"\n    :src=\"src\"\n    :alt=\"alt\"\n    :loading=\"loading\"\n    :referrerpolicy=\"referrerpolicy\"\n    :crossorigin=\"crossorigin\"\n    :class=\"cn('aspect-square size-full object-cover', props.class)\"\n    data-uipkge\n    data-slot=\"avatar-image\"\n    @error=\"handleError\"\n    @load=\"handleLoad\"\n  />\n  <slot v-else />\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/avatar/AvatarImage.vue"
    },
    {
      "path": "packages/registry-vue/components/avatar/avatar.variants.ts",
      "content": "import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\n/**\n * Variant definitions live in their own file (rather than the package\n * `index.ts` or inline in the SFC) so `Avatar.vue` / `AvatarFallback.vue`\n * can `import { avatarVariants } from './avatar.variants'` without\n * creating a circular dependency back through the index. The circular\n * form caused intermittent `$setup.avatarVariants is not a function`\n * errors during dev SSR.\n */\nexport const avatarVariants = cva('relative flex shrink-0 overflow-hidden', {\n  variants: {\n    size: {\n      xs: 'size-4',\n      sm: 'size-6',\n      default: 'size-8',\n      lg: 'size-12',\n      xl: 'size-16',\n      '2xl': 'size-20',\n    },\n    rounded: {\n      none: 'rounded-none',\n      sm: 'rounded-sm',\n      default: 'rounded-full',\n      md: 'rounded-md',\n      lg: 'rounded-lg',\n      xl: 'rounded-xl',\n      '2xl': 'rounded-2xl',\n      '3xl': 'rounded-3xl',\n      full: 'rounded-full',\n    },\n    color: {\n      default: '',\n      primary: 'bg-primary text-primary-foreground',\n      secondary: 'bg-secondary text-secondary-foreground',\n      destructive: 'bg-destructive text-destructive-foreground',\n      success: 'bg-[var(--success)] text-white dark:text-black',\n      warning: 'bg-[var(--warning)] text-black',\n      info: 'bg-[var(--info)] text-white dark:text-black',\n      error: 'bg-destructive text-white dark:text-black',\n      muted: 'bg-muted text-muted-foreground',\n    },\n    variant: {\n      default: '',\n      outlined: 'border-2 border-current',\n      soft: 'bg-opacity-20',\n    },\n  },\n  compoundVariants: [\n    { color: 'primary', variant: 'soft', class: 'bg-primary/20 text-primary' },\n    { color: 'secondary', variant: 'soft', class: 'bg-secondary/20 text-secondary-foreground' },\n    { color: 'destructive', variant: 'soft', class: 'bg-destructive/20 text-destructive' },\n    { color: 'success', variant: 'soft', class: 'bg-[var(--success)]/20 text-[var(--success)]' },\n    { color: 'warning', variant: 'soft', class: 'bg-[var(--warning)]/20 text-[var(--warning)]' },\n    { color: 'info', variant: 'soft', class: 'bg-[var(--info)]/20 text-[var(--info)]' },\n    { color: 'error', variant: 'soft', class: 'bg-destructive/20 text-destructive' },\n  ],\n  defaultVariants: {\n    size: 'default',\n    rounded: 'default',\n    color: 'default',\n  },\n})\n\nexport type AvatarVariants = VariantProps<typeof avatarVariants>\n\nexport const avatarFallbackVariants = cva(\n  'flex size-full items-center justify-center rounded-full bg-muted font-medium',\n  {\n    variants: {\n      size: {\n        xs: 'text-[8px]',\n        sm: 'text-xs',\n        default: 'text-sm',\n        lg: 'text-base',\n        xl: 'text-lg',\n        '2xl': 'text-xl',\n      },\n      color: {\n        default: '',\n        primary: 'bg-primary text-primary-foreground',\n        secondary: 'bg-secondary text-secondary-foreground',\n        destructive: 'bg-destructive text-destructive-foreground',\n        success: 'bg-[var(--success)] text-white dark:text-black',\n        warning: 'bg-[var(--warning)] text-black',\n        info: 'bg-[var(--info)] text-white dark:text-black',\n        error: 'bg-destructive text-white dark:text-black',\n        muted: 'bg-muted text-muted-foreground',\n      },\n    },\n    defaultVariants: {\n      size: 'default',\n      color: 'default',\n    },\n  },\n)\n\nexport type AvatarFallbackVariants = VariantProps<typeof avatarFallbackVariants>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/avatar/avatar.variants.ts"
    },
    {
      "path": "packages/registry-vue/components/avatar/index.ts",
      "content": "export { default as Avatar } from './Avatar.vue'\nexport { default as AvatarFallback } from './AvatarFallback.vue'\nexport { default as AvatarImage } from './AvatarImage.vue'\nexport { default as AvatarGroup } from './AvatarGroup.vue'\n\n// Re-export variant API from the sibling file (kept separate to avoid the\n// Avatar.vue <-> index.ts circular import that broke dev SSR).\nexport {\n  avatarVariants,\n  avatarFallbackVariants,\n  type AvatarVariants,\n  type AvatarFallbackVariants,\n} from './avatar.variants'\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/avatar/index.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Round or rounded-square user image with a fallback that shows initials or an icon when the image is missing or fails to load. Sizes from xs to 2xl, optional status dot, and a group composition for stacked avatar lists.",
  "categories": [
    "data-display"
  ]
}