Avatar Groups

Flexible avatar group components for displaying multiple avatars together. Supports various layouts, sizes, and customization options. previews

Stack Layout

Avatars stacked on top of each other with a slight overlap.

 
<div
  class="flex -space-x-4 grid-cols-4 grid-rows-1 gap-2 [&#38;>*]:z-0 [&#38;>*:hover]:z-10 w-fit mx-auto">
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 1" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 2" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 3" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 4" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 5" src="path/to/image.jpg">
    </div>
  </div>
</div>
---
import { AvatarGroup } from '@/components/ui/avatargroup';
import { Avatar } from '@/components/ui/avatar';
import sampleAvatar from '@/images/sample-avatar.jpg';
---

<AvatarGroup borderColor="blue">
<Avatar src={sampleAvatar} alt="User 1" bordered />
<Avatar src={sampleAvatar2} alt="User 2" bordered />
<Avatar src={sampleAvatar3} alt="User 3" bordered />
<Avatar src={sampleAvatar4} alt="User 4" bordered />
<Avatar src={sampleAvatar5} alt="User 5" bordered />
</AvatarGroup>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { colorPalette } from "@utils/colorUtils";
import { twMerge } from "tailwind-merge";

// Types and Interfaces
type AvatarGroupVariants = VariantProps<typeof avatarGroupStyles>;

interface Props extends HTMLAttributes<"div">, AvatarGroupVariants {
  layout?: "stack" | "grid";
  columns?: number;
  rows?: number;
  spacing?: "tight" | "normal" | "loose";
  borderColor?: keyof typeof colorPalette;
  class?: string;
  preview?: boolean;
}

// Styles
const avatarGroupStyles = tv({
  base: "flex",
  variants: {
    layout: {
      stack: "-space-x-4",
      grid: "grid w-fit",
    },
    columns: {
      1: "grid-cols-1",
      2: "grid-cols-2",
      3: "grid-cols-3",
      4: "grid-cols-4",
      5: "grid-cols-5",
      6: "grid-cols-6",
    },
    rows: {
      1: "grid-rows-1",
      2: "grid-rows-2",
      3: "grid-rows-3",
      4: "grid-rows-4",
      5: "grid-rows-5",
      6: "grid-rows-6",
    },
    spacing: {
      tight: "gap-1",
      normal: "gap-2",
      loose: "gap-3",
    },
    preview: {
      true: "items-center gap-12 mx-auto w-fit p-4",
      false: "",
    },
  },
  defaultVariants: {
    layout: "stack",
    columns: 4,
    rows: 1,
    spacing: "normal",
    preview: false,
  },
  compoundVariants: [
    {
      layout: "stack",
      class: "[&>*]:z-0 [&>*:hover]:z-10 w-fit mx-auto",
    },
  ],
});

// Component Logic
const {
  layout = "stack",
  columns = 4,
  rows = 1,
  spacing = "normal",
  class: className = "",
  preview = false,
} = Astro.props as Props;

const containerClasses = twMerge(
  avatarGroupStyles({ layout, columns, rows, spacing, preview }),
  className,
);

// PropTypes for documentation
export const propTypes = {
  layout: {
    type: ["stack", "grid"],
    description: "Layout of the avatar group",
    default: "stack",
  },
  columns: {
    type: "number",
    description: "Number of columns for grid layout",
    default: 4,
  },
  rows: {
    type: "number",
    description: "Number of rows for grid layout",
    default: 1,
  },
  spacing: {
    type: ["tight", "normal", "loose"],
    description: "Spacing between avatars",
    default: "normal",
  },
  borderColor: {
    type: Object.keys(colorPalette),
    description: "Color of the avatar border",
    default: "white",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the avatar group",
  },
  preview: {
    type: "boolean",
    description: "Whether to apply preview styling",
    default: false,
  },
};
---

<div class={containerClasses}>
  <slot />
</div>

Stacked Avatars with Different Sizes

Avatar group with stacked avatars of various sizes.

 
<div
  class="flex -space-x-4 grid-cols-4 grid-rows-1 gap-2 [&#38;>*]:z-0 [&#38;>*:hover]:z-10 w-fit mx-auto">
  <div class="relative flex items-center justify-center rounded-full size-6 text-xs">
    <div
      class="relative flex items-center justify-center rounded-full size-6 text-xs overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-6 text-xs">
    <div
      class="relative flex items-center justify-center rounded-full size-6 text-xs overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-6 text-xs">
    <div
      class="relative flex items-center justify-center rounded-full size-6 text-xs overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-6 text-xs">
    <div
      class="relative flex items-center justify-center rounded-full size-6 text-xs overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-6 text-xs">
    <div
      class="relative flex items-center justify-center rounded-full size-6 text-xs overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
</div>
<div
  class="flex -space-x-4 grid-cols-4 grid-rows-1 gap-2 [&#38;>*]:z-0 [&#38;>*:hover]:z-10 w-fit mx-auto mt-4">
  <div class="relative flex items-center justify-center rounded-full size-8 text-sm">
    <div
      class="relative flex items-center justify-center rounded-full size-8 text-sm overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-8 text-sm">
    <div
      class="relative flex items-center justify-center rounded-full size-8 text-sm overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-8 text-sm">
    <div
      class="relative flex items-center justify-center rounded-full size-8 text-sm overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-8 text-sm">
    <div
      class="relative flex items-center justify-center rounded-full size-8 text-sm overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-8 text-sm">
    <div
      class="relative flex items-center justify-center rounded-full size-8 text-sm overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
</div>
<div
  class="flex -space-x-4 grid-cols-4 grid-rows-1 gap-2 [&#38;>*]:z-0 [&#38;>*:hover]:z-10 w-fit mx-auto mt-4">
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
</div>
<div
  class="flex -space-x-4 grid-cols-4 grid-rows-1 gap-2 [&#38;>*]:z-0 [&#38;>*:hover]:z-10 w-fit mx-auto mt-4">
  <div class="relative flex items-center justify-center rounded-full size-14 text-lg">
    <div
      class="relative flex items-center justify-center rounded-full size-14 text-lg overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-14 text-lg">
    <div
      class="relative flex items-center justify-center rounded-full size-14 text-lg overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-14 text-lg">
    <div
      class="relative flex items-center justify-center rounded-full size-14 text-lg overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-14 text-lg">
    <div
      class="relative flex items-center justify-center rounded-full size-14 text-lg overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-14 text-lg">
    <div
      class="relative flex items-center justify-center rounded-full size-14 text-lg overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
</div>
<div
  class="flex -space-x-4 grid-cols-4 grid-rows-1 gap-2 [&#38;>*]:z-0 [&#38;>*:hover]:z-10 w-fit mx-auto mt-4">
  <div class="relative flex items-center justify-center rounded-full size-16 text-xl">
    <div
      class="relative flex items-center justify-center rounded-full size-16 text-xl overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-16 text-xl">
    <div
      class="relative flex items-center justify-center rounded-full size-16 text-xl overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-16 text-xl">
    <div
      class="relative flex items-center justify-center rounded-full size-16 text-xl overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-16 text-xl">
    <div
      class="relative flex items-center justify-center rounded-full size-16 text-xl overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-16 text-xl">
    <div
      class="relative flex items-center justify-center rounded-full size-16 text-xl overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
</div>
<div
  class="flex -space-x-4 grid-cols-4 grid-rows-1 gap-2 [&#38;>*]:z-0 [&#38;>*:hover]:z-10 w-fit mx-auto mt-4">
  <div class="relative flex items-center justify-center rounded-full size-20 text-2xl">
    <div
      class="relative flex items-center justify-center rounded-full size-20 text-2xl overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-20 text-2xl">
    <div
      class="relative flex items-center justify-center rounded-full size-20 text-2xl overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-20 text-2xl">
    <div
      class="relative flex items-center justify-center rounded-full size-20 text-2xl overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-20 text-2xl">
    <div
      class="relative flex items-center justify-center rounded-full size-20 text-2xl overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-20 text-2xl">
    <div
      class="relative flex items-center justify-center rounded-full size-20 text-2xl overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="Circular" src="path/to/image.jpg">
    </div>
  </div>
</div>
---
import { AvatarGroup } from '@/components/ui/avatargroup';
import { Avatar } from '@/components/ui/avatar';
import sampleAvatar from '@/images/sample-avatar.jpg';
---

<AvatarGroup>
<Avatar src={sampleAvatar} alt="Circular" shape="circular" size="xs" bordered />
<Avatar src={sampleAvatar2} alt="Circular" shape="circular" size="xs" bordered />
<Avatar src={sampleAvatar3} alt="Circular" shape="circular" size="xs" bordered />
<Avatar src={sampleAvatar4} alt="Circular" shape="circular" size="xs" bordered />
<Avatar src={sampleAvatar5} alt="Circular" shape="circular" size="xs" bordered />
</AvatarGroup>

<AvatarGroup class="mt-4">
<Avatar src={sampleAvatar} alt="Circular" shape="circular" size="sm" bordered />
<Avatar src={sampleAvatar2} alt="Circular" shape="circular" size="sm" bordered />
<Avatar src={sampleAvatar3} alt="Circular" shape="circular" size="sm" bordered />
<Avatar src={sampleAvatar4} alt="Circular" shape="circular" size="sm" bordered />
<Avatar src={sampleAvatar5} alt="Circular" shape="circular" size="sm" bordered />
</AvatarGroup>

<AvatarGroup class="mt-4">
<Avatar src={sampleAvatar} alt="Circular" shape="circular" size="md" bordered />
<Avatar src={sampleAvatar2} alt="Circular" shape="circular" size="md" bordered />
<Avatar src={sampleAvatar3} alt="Circular" shape="circular" size="md" bordered />
<Avatar src={sampleAvatar4} alt="Circular" shape="circular" size="md" bordered />
<Avatar src={sampleAvatar5} alt="Circular" shape="circular" size="md" bordered />
</AvatarGroup>

<AvatarGroup class="mt-4">
<Avatar src={sampleAvatar} alt="Circular" shape="circular" size="lg" bordered />
<Avatar src={sampleAvatar2} alt="Circular" shape="circular" size="lg" bordered />
<Avatar src={sampleAvatar3} alt="Circular" shape="circular" size="lg" bordered />
<Avatar src={sampleAvatar4} alt="Circular" shape="circular" size="lg" bordered />
<Avatar src={sampleAvatar5} alt="Circular" shape="circular" size="lg" bordered />
</AvatarGroup>

<AvatarGroup class="mt-4">
<Avatar src={sampleAvatar} alt="Circular" shape="circular" size="xl" bordered />
<Avatar src={sampleAvatar2} alt="Circular" shape="circular" size="xl" bordered />
<Avatar src={sampleAvatar3} alt="Circular" shape="circular" size="xl" bordered />
<Avatar src={sampleAvatar4} alt="Circular" shape="circular" size="xl" bordered />
<Avatar src={sampleAvatar5} alt="Circular" shape="circular" size="xl" bordered />
</AvatarGroup>

<AvatarGroup class="mt-4">
<Avatar src={sampleAvatar} alt="Circular" shape="circular" size="2xl" bordered />
<Avatar src={sampleAvatar2} alt="Circular" shape="circular" size="2xl" bordered />
<Avatar src={sampleAvatar3} alt="Circular" shape="circular" size="2xl" bordered />
<Avatar src={sampleAvatar4} alt="Circular" shape="circular" size="2xl" bordered />
<Avatar src={sampleAvatar5} alt="Circular" shape="circular" size="2xl" bordered />
</AvatarGroup>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { colorPalette } from "@utils/colorUtils";
import { twMerge } from "tailwind-merge";

// Types and Interfaces
type AvatarGroupVariants = VariantProps<typeof avatarGroupStyles>;

interface Props extends HTMLAttributes<"div">, AvatarGroupVariants {
  layout?: "stack" | "grid";
  columns?: number;
  rows?: number;
  spacing?: "tight" | "normal" | "loose";
  borderColor?: keyof typeof colorPalette;
  class?: string;
  preview?: boolean;
}

// Styles
const avatarGroupStyles = tv({
  base: "flex",
  variants: {
    layout: {
      stack: "-space-x-4",
      grid: "grid w-fit",
    },
    columns: {
      1: "grid-cols-1",
      2: "grid-cols-2",
      3: "grid-cols-3",
      4: "grid-cols-4",
      5: "grid-cols-5",
      6: "grid-cols-6",
    },
    rows: {
      1: "grid-rows-1",
      2: "grid-rows-2",
      3: "grid-rows-3",
      4: "grid-rows-4",
      5: "grid-rows-5",
      6: "grid-rows-6",
    },
    spacing: {
      tight: "gap-1",
      normal: "gap-2",
      loose: "gap-3",
    },
    preview: {
      true: "items-center gap-12 mx-auto w-fit p-4",
      false: "",
    },
  },
  defaultVariants: {
    layout: "stack",
    columns: 4,
    rows: 1,
    spacing: "normal",
    preview: false,
  },
  compoundVariants: [
    {
      layout: "stack",
      class: "[&>*]:z-0 [&>*:hover]:z-10 w-fit mx-auto",
    },
  ],
});

// Component Logic
const {
  layout = "stack",
  columns = 4,
  rows = 1,
  spacing = "normal",
  class: className = "",
  preview = false,
} = Astro.props as Props;

const containerClasses = twMerge(
  avatarGroupStyles({ layout, columns, rows, spacing, preview }),
  className,
);

// PropTypes for documentation
export const propTypes = {
  layout: {
    type: ["stack", "grid"],
    description: "Layout of the avatar group",
    default: "stack",
  },
  columns: {
    type: "number",
    description: "Number of columns for grid layout",
    default: 4,
  },
  rows: {
    type: "number",
    description: "Number of rows for grid layout",
    default: 1,
  },
  spacing: {
    type: ["tight", "normal", "loose"],
    description: "Spacing between avatars",
    default: "normal",
  },
  borderColor: {
    type: Object.keys(colorPalette),
    description: "Color of the avatar border",
    default: "white",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the avatar group",
  },
  preview: {
    type: "boolean",
    description: "Whether to apply preview styling",
    default: false,
  },
};
---

<div class={containerClasses}>
  <slot />
</div>

Grid Layout

Avatars arranged in a grid layout.

 
<div class="grid w-fit grid-cols-3 grid-rows-2 gap-2">
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
      <img width="400" height="400" alt="User 1" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
      <img width="400" height="400" alt="User 2" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
      <img width="400" height="400" alt="User 3" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
      <img width="400" height="400" alt="User 4" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
      <img width="400" height="400" alt="User 5" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
      <img width="400" height="400" alt="User 6" src="path/to/image.jpg">
    </div>
  </div>
</div>
---
import { AvatarGroup } from '@/components/ui/avatargroup';
import { Avatar } from '@/components/ui/avatar';
import sampleAvatar from '@/images/sample-avatar.jpg';
---

<AvatarGroup layout="grid" columns={3} rows={2}>
<Avatar src={sampleAvatar} alt="User 1" />
<Avatar src={sampleAvatar2} alt="User 2" />
<Avatar src={sampleAvatar3} alt="User 3" />
<Avatar src={sampleAvatar4} alt="User 4" />
<Avatar src={sampleAvatar5} alt="User 5" />
<Avatar src={sampleAvatar} alt="User 6" />
</AvatarGroup>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { colorPalette } from "@utils/colorUtils";
import { twMerge } from "tailwind-merge";

// Types and Interfaces
type AvatarGroupVariants = VariantProps<typeof avatarGroupStyles>;

interface Props extends HTMLAttributes<"div">, AvatarGroupVariants {
  layout?: "stack" | "grid";
  columns?: number;
  rows?: number;
  spacing?: "tight" | "normal" | "loose";
  borderColor?: keyof typeof colorPalette;
  class?: string;
  preview?: boolean;
}

// Styles
const avatarGroupStyles = tv({
  base: "flex",
  variants: {
    layout: {
      stack: "-space-x-4",
      grid: "grid w-fit",
    },
    columns: {
      1: "grid-cols-1",
      2: "grid-cols-2",
      3: "grid-cols-3",
      4: "grid-cols-4",
      5: "grid-cols-5",
      6: "grid-cols-6",
    },
    rows: {
      1: "grid-rows-1",
      2: "grid-rows-2",
      3: "grid-rows-3",
      4: "grid-rows-4",
      5: "grid-rows-5",
      6: "grid-rows-6",
    },
    spacing: {
      tight: "gap-1",
      normal: "gap-2",
      loose: "gap-3",
    },
    preview: {
      true: "items-center gap-12 mx-auto w-fit p-4",
      false: "",
    },
  },
  defaultVariants: {
    layout: "stack",
    columns: 4,
    rows: 1,
    spacing: "normal",
    preview: false,
  },
  compoundVariants: [
    {
      layout: "stack",
      class: "[&>*]:z-0 [&>*:hover]:z-10 w-fit mx-auto",
    },
  ],
});

// Component Logic
const {
  layout = "stack",
  columns = 4,
  rows = 1,
  spacing = "normal",
  class: className = "",
  preview = false,
} = Astro.props as Props;

const containerClasses = twMerge(
  avatarGroupStyles({ layout, columns, rows, spacing, preview }),
  className,
);

// PropTypes for documentation
export const propTypes = {
  layout: {
    type: ["stack", "grid"],
    description: "Layout of the avatar group",
    default: "stack",
  },
  columns: {
    type: "number",
    description: "Number of columns for grid layout",
    default: 4,
  },
  rows: {
    type: "number",
    description: "Number of rows for grid layout",
    default: 1,
  },
  spacing: {
    type: ["tight", "normal", "loose"],
    description: "Spacing between avatars",
    default: "normal",
  },
  borderColor: {
    type: Object.keys(colorPalette),
    description: "Color of the avatar border",
    default: "white",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the avatar group",
  },
  preview: {
    type: "boolean",
    description: "Whether to apply preview styling",
    default: false,
  },
};
---

<div class={containerClasses}>
  <slot />
</div>

Grid with Different Columns and Rows

Avatar groups in grid layout with various column and row counts.

 
<div class="space-y-8">
  <div class="grid w-fit grid-cols-2 grid-rows-2 gap-2">
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
        <img width="400" height="400" alt="User 1" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
        <img width="400" height="400" alt="User 2" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
        <img width="400" height="400" alt="User 3" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
        <img width="400" height="400" alt="User 4" src="path/to/image.jpg">
      </div>
    </div>
  </div>
  <div class="grid w-fit grid-cols-4 grid-rows-1 gap-2">
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
        <img width="400" height="400" alt="User 1" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
        <img width="400" height="400" alt="User 2" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
        <img width="400" height="400" alt="User 3" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
        <img width="400" height="400" alt="User 4" src="path/to/image.jpg">
      </div>
    </div>
  </div>
  <div class="grid w-fit grid-cols-3 grid-rows-3 gap-2">
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
        <img width="400" height="400" alt="User 1" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
        <img width="400" height="400" alt="User 2" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
        <img width="400" height="400" alt="User 3" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
        <img width="400" height="400" alt="User 4" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
        <img width="400" height="400" alt="User 5" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
        <img width="400" height="400" alt="User 6" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
        <img width="400" height="400" alt="User 7" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
        <img width="400" height="400" alt="User 8" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
        <img width="400" height="400" alt="User 9" src="path/to/image.jpg">
      </div>
    </div>
  </div>
</div>
---
import { AvatarGroup } from '@/components/ui/avatargroup';
import { Avatar } from '@/components/ui/avatar';
import sampleAvatar from '@/images/sample-avatar.jpg';
---

<AvatarGroup layout="grid" columns={2} rows={2}>
<Avatar src={sampleAvatar} alt="User 1" />
<Avatar src={sampleAvatar2} alt="User 2" />
<Avatar src={sampleAvatar3} alt="User 3" />
<Avatar src={sampleAvatar4} alt="User 4" />
</AvatarGroup>

<AvatarGroup layout="grid" columns={4} rows={1}>
<Avatar src={sampleAvatar} alt="User 1" />
<Avatar src={sampleAvatar2} alt="User 2" />
<Avatar src={sampleAvatar3} alt="User 3" />
<Avatar src={sampleAvatar4} alt="User 4" />
</AvatarGroup>

<AvatarGroup layout="grid" columns={3} rows={3}>
<Avatar src={sampleAvatar} alt="User 1" />
<Avatar src={sampleAvatar2} alt="User 2" />
<Avatar src={sampleAvatar3} alt="User 3" />
<Avatar src={sampleAvatar4} alt="User 4" />
<Avatar src={sampleAvatar5} alt="User 5" />
<Avatar src={sampleAvatar} alt="User 6" />
<Avatar src={sampleAvatar} alt="User 7" />
<Avatar src={sampleAvatar} alt="User 8" />
<Avatar src={sampleAvatar} alt="User 9" />
</AvatarGroup>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { colorPalette } from "@utils/colorUtils";
import { twMerge } from "tailwind-merge";

// Types and Interfaces
type AvatarGroupVariants = VariantProps<typeof avatarGroupStyles>;

interface Props extends HTMLAttributes<"div">, AvatarGroupVariants {
  layout?: "stack" | "grid";
  columns?: number;
  rows?: number;
  spacing?: "tight" | "normal" | "loose";
  borderColor?: keyof typeof colorPalette;
  class?: string;
  preview?: boolean;
}

// Styles
const avatarGroupStyles = tv({
  base: "flex",
  variants: {
    layout: {
      stack: "-space-x-4",
      grid: "grid w-fit",
    },
    columns: {
      1: "grid-cols-1",
      2: "grid-cols-2",
      3: "grid-cols-3",
      4: "grid-cols-4",
      5: "grid-cols-5",
      6: "grid-cols-6",
    },
    rows: {
      1: "grid-rows-1",
      2: "grid-rows-2",
      3: "grid-rows-3",
      4: "grid-rows-4",
      5: "grid-rows-5",
      6: "grid-rows-6",
    },
    spacing: {
      tight: "gap-1",
      normal: "gap-2",
      loose: "gap-3",
    },
    preview: {
      true: "items-center gap-12 mx-auto w-fit p-4",
      false: "",
    },
  },
  defaultVariants: {
    layout: "stack",
    columns: 4,
    rows: 1,
    spacing: "normal",
    preview: false,
  },
  compoundVariants: [
    {
      layout: "stack",
      class: "[&>*]:z-0 [&>*:hover]:z-10 w-fit mx-auto",
    },
  ],
});

// Component Logic
const {
  layout = "stack",
  columns = 4,
  rows = 1,
  spacing = "normal",
  class: className = "",
  preview = false,
} = Astro.props as Props;

const containerClasses = twMerge(
  avatarGroupStyles({ layout, columns, rows, spacing, preview }),
  className,
);

// PropTypes for documentation
export const propTypes = {
  layout: {
    type: ["stack", "grid"],
    description: "Layout of the avatar group",
    default: "stack",
  },
  columns: {
    type: "number",
    description: "Number of columns for grid layout",
    default: 4,
  },
  rows: {
    type: "number",
    description: "Number of rows for grid layout",
    default: 1,
  },
  spacing: {
    type: ["tight", "normal", "loose"],
    description: "Spacing between avatars",
    default: "normal",
  },
  borderColor: {
    type: Object.keys(colorPalette),
    description: "Color of the avatar border",
    default: "white",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the avatar group",
  },
  preview: {
    type: "boolean",
    description: "Whether to apply preview styling",
    default: false,
  },
};
---

<div class={containerClasses}>
  <slot />
</div>

Spacing Options

Different spacing options for avatar groups.

 
<div class="space-y-8">
  <div
    class="flex -space-x-4 grid-cols-4 grid-rows-1 gap-1 [&#38;>*]:z-0 [&#38;>*:hover]:z-10 w-fit mx-auto">
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
        <img width="400" height="400" alt="User 1" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
        <img width="400" height="400" alt="User 2" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
        <img width="400" height="400" alt="User 3" src="path/to/image.jpg">
      </div>
    </div>
  </div>
  <div
    class="flex -space-x-4 grid-cols-4 grid-rows-1 gap-2 [&#38;>*]:z-0 [&#38;>*:hover]:z-10 w-fit mx-auto">
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
        <img width="400" height="400" alt="User 1" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
        <img width="400" height="400" alt="User 2" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
        <img width="400" height="400" alt="User 3" src="path/to/image.jpg">
      </div>
    </div>
  </div>
  <div
    class="flex -space-x-4 grid-cols-4 grid-rows-1 gap-3 [&#38;>*]:z-0 [&#38;>*:hover]:z-10 w-fit mx-auto">
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
        <img width="400" height="400" alt="User 1" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
        <img width="400" height="400" alt="User 2" src="path/to/image.jpg">
      </div>
    </div>
    <div class="relative flex items-center justify-center rounded-full size-12 text-base">
      <div
        class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
        <img width="400" height="400" alt="User 3" src="path/to/image.jpg">
      </div>
    </div>
  </div>
</div>
---
import { AvatarGroup } from '@/components/ui/avatargroup';
import { Avatar } from '@/components/ui/avatar';
import sampleAvatar from '@/images/sample-avatar.jpg';
---

<AvatarGroup spacing="tight">
<Avatar src={sampleAvatar} alt="User 1" bordered />
<Avatar src={sampleAvatar2} alt="User 2" bordered />
<Avatar src={sampleAvatar3} alt="User 3" bordered />
</AvatarGroup>

<AvatarGroup spacing="normal">
<Avatar src={sampleAvatar} alt="User 1" bordered />
<Avatar src={sampleAvatar2} alt="User 2" bordered />
<Avatar src={sampleAvatar3} alt="User 3" bordered />
</AvatarGroup>

<AvatarGroup spacing="loose">
<Avatar src={sampleAvatar} alt="User 1" bordered />
<Avatar src={sampleAvatar2} alt="User 2" bordered />
<Avatar src={sampleAvatar3} alt="User 3" bordered />
</AvatarGroup>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { colorPalette } from "@utils/colorUtils";
import { twMerge } from "tailwind-merge";

// Types and Interfaces
type AvatarGroupVariants = VariantProps<typeof avatarGroupStyles>;

interface Props extends HTMLAttributes<"div">, AvatarGroupVariants {
  layout?: "stack" | "grid";
  columns?: number;
  rows?: number;
  spacing?: "tight" | "normal" | "loose";
  borderColor?: keyof typeof colorPalette;
  class?: string;
  preview?: boolean;
}

// Styles
const avatarGroupStyles = tv({
  base: "flex",
  variants: {
    layout: {
      stack: "-space-x-4",
      grid: "grid w-fit",
    },
    columns: {
      1: "grid-cols-1",
      2: "grid-cols-2",
      3: "grid-cols-3",
      4: "grid-cols-4",
      5: "grid-cols-5",
      6: "grid-cols-6",
    },
    rows: {
      1: "grid-rows-1",
      2: "grid-rows-2",
      3: "grid-rows-3",
      4: "grid-rows-4",
      5: "grid-rows-5",
      6: "grid-rows-6",
    },
    spacing: {
      tight: "gap-1",
      normal: "gap-2",
      loose: "gap-3",
    },
    preview: {
      true: "items-center gap-12 mx-auto w-fit p-4",
      false: "",
    },
  },
  defaultVariants: {
    layout: "stack",
    columns: 4,
    rows: 1,
    spacing: "normal",
    preview: false,
  },
  compoundVariants: [
    {
      layout: "stack",
      class: "[&>*]:z-0 [&>*:hover]:z-10 w-fit mx-auto",
    },
  ],
});

// Component Logic
const {
  layout = "stack",
  columns = 4,
  rows = 1,
  spacing = "normal",
  class: className = "",
  preview = false,
} = Astro.props as Props;

const containerClasses = twMerge(
  avatarGroupStyles({ layout, columns, rows, spacing, preview }),
  className,
);

// PropTypes for documentation
export const propTypes = {
  layout: {
    type: ["stack", "grid"],
    description: "Layout of the avatar group",
    default: "stack",
  },
  columns: {
    type: "number",
    description: "Number of columns for grid layout",
    default: 4,
  },
  rows: {
    type: "number",
    description: "Number of rows for grid layout",
    default: 1,
  },
  spacing: {
    type: ["tight", "normal", "loose"],
    description: "Spacing between avatars",
    default: "normal",
  },
  borderColor: {
    type: Object.keys(colorPalette),
    description: "Color of the avatar border",
    default: "white",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the avatar group",
  },
  preview: {
    type: "boolean",
    description: "Whether to apply preview styling",
    default: false,
  },
};
---

<div class={containerClasses}>
  <slot />
</div>

Border Color

Customizable border color for stacked avatars.

 
<div
  class="flex -space-x-4 grid-cols-4 grid-rows-1 gap-2 [&#38;>*]:z-0 [&#38;>*:hover]:z-10 w-fit mx-auto">
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-purple-600 dark:border-purple-500">
      <img width="400" height="400" alt="User 1" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-indigo-600 dark:border-indigo-500">
      <img width="400" height="400" alt="User 2" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-green-600 dark:border-green-500">
      <img width="400" height="400" alt="User 3" src="path/to/image.jpg">
    </div>
  </div>
</div>
---
import { AvatarGroup } from '@/components/ui/avatargroup';
import { Avatar } from '@/components/ui/avatar';
import sampleAvatar from '@/images/sample-avatar.jpg';
---

<AvatarGroup borderColor="red">
<Avatar src={sampleAvatar} alt="User 1" bordered borderColor="purple" />
<Avatar src={sampleAvatar2} alt="User 2" bordered borderColor="indigo" />
<Avatar src={sampleAvatar3} alt="User 3" bordered borderColor="green" />
</AvatarGroup>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { colorPalette } from "@utils/colorUtils";
import { twMerge } from "tailwind-merge";

// Types and Interfaces
type AvatarGroupVariants = VariantProps<typeof avatarGroupStyles>;

interface Props extends HTMLAttributes<"div">, AvatarGroupVariants {
  layout?: "stack" | "grid";
  columns?: number;
  rows?: number;
  spacing?: "tight" | "normal" | "loose";
  borderColor?: keyof typeof colorPalette;
  class?: string;
  preview?: boolean;
}

// Styles
const avatarGroupStyles = tv({
  base: "flex",
  variants: {
    layout: {
      stack: "-space-x-4",
      grid: "grid w-fit",
    },
    columns: {
      1: "grid-cols-1",
      2: "grid-cols-2",
      3: "grid-cols-3",
      4: "grid-cols-4",
      5: "grid-cols-5",
      6: "grid-cols-6",
    },
    rows: {
      1: "grid-rows-1",
      2: "grid-rows-2",
      3: "grid-rows-3",
      4: "grid-rows-4",
      5: "grid-rows-5",
      6: "grid-rows-6",
    },
    spacing: {
      tight: "gap-1",
      normal: "gap-2",
      loose: "gap-3",
    },
    preview: {
      true: "items-center gap-12 mx-auto w-fit p-4",
      false: "",
    },
  },
  defaultVariants: {
    layout: "stack",
    columns: 4,
    rows: 1,
    spacing: "normal",
    preview: false,
  },
  compoundVariants: [
    {
      layout: "stack",
      class: "[&>*]:z-0 [&>*:hover]:z-10 w-fit mx-auto",
    },
  ],
});

// Component Logic
const {
  layout = "stack",
  columns = 4,
  rows = 1,
  spacing = "normal",
  class: className = "",
  preview = false,
} = Astro.props as Props;

const containerClasses = twMerge(
  avatarGroupStyles({ layout, columns, rows, spacing, preview }),
  className,
);

// PropTypes for documentation
export const propTypes = {
  layout: {
    type: ["stack", "grid"],
    description: "Layout of the avatar group",
    default: "stack",
  },
  columns: {
    type: "number",
    description: "Number of columns for grid layout",
    default: 4,
  },
  rows: {
    type: "number",
    description: "Number of rows for grid layout",
    default: 1,
  },
  spacing: {
    type: ["tight", "normal", "loose"],
    description: "Spacing between avatars",
    default: "normal",
  },
  borderColor: {
    type: Object.keys(colorPalette),
    description: "Color of the avatar border",
    default: "white",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the avatar group",
  },
  preview: {
    type: "boolean",
    description: "Whether to apply preview styling",
    default: false,
  },
};
---

<div class={containerClasses}>
  <slot />
</div>

Mixed Avatar Types

Avatar group with different types of avatars.

 
<div
  class="flex -space-x-4 grid-cols-4 grid-rows-1 gap-2 [&#38;>*]:z-0 [&#38;>*:hover]:z-10 w-fit mx-auto">
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 1" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <span class="font-medium">JD</span>
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <span class="inline-block size-5">
        <svg
          xmlns="http://www.w3.org/2000/svg"
          viewBox="0 0 24 24"
          fill="currentColor"
          aria-hidden="true"
          data-slot="icon">
          <path
            fill-rule="evenodd"
            d="M7.5 6a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM3.751 20.105a8.25 8.25 0 0 1 16.498 0 .75.75 0 0 1-.437.695A18.683 18.683 0 0 1 12 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 0 1-.437-.695Z"
            clip-rule="evenodd"></path>
        </svg>
      </span>
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 4" src="path/to/image.jpg">
    </div>
    <span
      class="absolute w-3 h-3 bottom-[2%] right-[2%] bg-green-500 rounded-full border-2 border-white dark:border-gray-800"></span>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-red-500 dark:bg-red-600 text-white dark:text-white hover:text-red-100 dark:hover:text-red-200 border-2 border-white dark:border-gray-800">
      <span class="font-medium">AB</span>
    </div>
  </div>
</div>
---
import { AvatarGroup } from '@/components/ui/avatargroup';
import { Avatar } from '@/components/ui/avatar';
import sampleAvatar from '@/images/sample-avatar.jpg';
---

<AvatarGroup>
<Avatar src={sampleAvatar} alt="User 1" bordered />
<Avatar initials="JD" bordered />
<Avatar icon="UserIcon" bordered />
<Avatar src={sampleAvatar2} alt="User 4" status="online" bordered />
<Avatar initials="AB" color="red" bordered />
</AvatarGroup>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { colorPalette } from "@utils/colorUtils";
import { twMerge } from "tailwind-merge";

// Types and Interfaces
type AvatarGroupVariants = VariantProps<typeof avatarGroupStyles>;

interface Props extends HTMLAttributes<"div">, AvatarGroupVariants {
  layout?: "stack" | "grid";
  columns?: number;
  rows?: number;
  spacing?: "tight" | "normal" | "loose";
  borderColor?: keyof typeof colorPalette;
  class?: string;
  preview?: boolean;
}

// Styles
const avatarGroupStyles = tv({
  base: "flex",
  variants: {
    layout: {
      stack: "-space-x-4",
      grid: "grid w-fit",
    },
    columns: {
      1: "grid-cols-1",
      2: "grid-cols-2",
      3: "grid-cols-3",
      4: "grid-cols-4",
      5: "grid-cols-5",
      6: "grid-cols-6",
    },
    rows: {
      1: "grid-rows-1",
      2: "grid-rows-2",
      3: "grid-rows-3",
      4: "grid-rows-4",
      5: "grid-rows-5",
      6: "grid-rows-6",
    },
    spacing: {
      tight: "gap-1",
      normal: "gap-2",
      loose: "gap-3",
    },
    preview: {
      true: "items-center gap-12 mx-auto w-fit p-4",
      false: "",
    },
  },
  defaultVariants: {
    layout: "stack",
    columns: 4,
    rows: 1,
    spacing: "normal",
    preview: false,
  },
  compoundVariants: [
    {
      layout: "stack",
      class: "[&>*]:z-0 [&>*:hover]:z-10 w-fit mx-auto",
    },
  ],
});

// Component Logic
const {
  layout = "stack",
  columns = 4,
  rows = 1,
  spacing = "normal",
  class: className = "",
  preview = false,
} = Astro.props as Props;

const containerClasses = twMerge(
  avatarGroupStyles({ layout, columns, rows, spacing, preview }),
  className,
);

// PropTypes for documentation
export const propTypes = {
  layout: {
    type: ["stack", "grid"],
    description: "Layout of the avatar group",
    default: "stack",
  },
  columns: {
    type: "number",
    description: "Number of columns for grid layout",
    default: 4,
  },
  rows: {
    type: "number",
    description: "Number of rows for grid layout",
    default: 1,
  },
  spacing: {
    type: ["tight", "normal", "loose"],
    description: "Spacing between avatars",
    default: "normal",
  },
  borderColor: {
    type: Object.keys(colorPalette),
    description: "Color of the avatar border",
    default: "white",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the avatar group",
  },
  preview: {
    type: "boolean",
    description: "Whether to apply preview styling",
    default: false,
  },
};
---

<div class={containerClasses}>
  <slot />
</div>

Stack with Avatars with Status

Avatar group with status indicators.

 
<div
  class="flex -space-x-4 grid-cols-4 grid-rows-1 gap-2 [&#38;>*]:z-0 [&#38;>*:hover]:z-10 w-fit mx-auto">
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 1" src="path/to/image.jpg">
    </div>
    <span
      class="absolute w-3 h-3 bottom-[2%] right-[2%] bg-green-500 rounded-full border-2 border-white dark:border-gray-800"></span>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 2" src="path/to/image.jpg">
    </div>
    <span
      class="absolute w-3 h-3 bottom-[2%] right-[2%] bg-gray-500 rounded-full border-2 border-white dark:border-gray-800"></span>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 3" src="path/to/image.jpg">
    </div>
    <span
      class="absolute w-3 h-3 bottom-[2%] right-[2%] bg-red-500 rounded-full border-2 border-white dark:border-gray-800"></span>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 4" src="path/to/image.jpg">
    </div>
    <span
      class="absolute w-3 h-3 bottom-[2%] right-[2%] bg-yellow-500 rounded-full border-2 border-white dark:border-gray-800"></span>
  </div>
</div>
---
import { AvatarGroup } from '@/components/ui/avatargroup';
import { Avatar } from '@/components/ui/avatar';
import sampleAvatar from '@/images/sample-avatar.jpg';
---

<AvatarGroup>
<Avatar src={sampleAvatar} alt="User 1" status="online" bordered />
<Avatar src={sampleAvatar2} alt="User 2" status="offline" bordered />
<Avatar src={sampleAvatar3} alt="User 3" status="busy" bordered />
<Avatar src={sampleAvatar4} alt="User 4" status="away" bordered />
</AvatarGroup>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { colorPalette } from "@utils/colorUtils";
import { twMerge } from "tailwind-merge";

// Types and Interfaces
type AvatarGroupVariants = VariantProps<typeof avatarGroupStyles>;

interface Props extends HTMLAttributes<"div">, AvatarGroupVariants {
  layout?: "stack" | "grid";
  columns?: number;
  rows?: number;
  spacing?: "tight" | "normal" | "loose";
  borderColor?: keyof typeof colorPalette;
  class?: string;
  preview?: boolean;
}

// Styles
const avatarGroupStyles = tv({
  base: "flex",
  variants: {
    layout: {
      stack: "-space-x-4",
      grid: "grid w-fit",
    },
    columns: {
      1: "grid-cols-1",
      2: "grid-cols-2",
      3: "grid-cols-3",
      4: "grid-cols-4",
      5: "grid-cols-5",
      6: "grid-cols-6",
    },
    rows: {
      1: "grid-rows-1",
      2: "grid-rows-2",
      3: "grid-rows-3",
      4: "grid-rows-4",
      5: "grid-rows-5",
      6: "grid-rows-6",
    },
    spacing: {
      tight: "gap-1",
      normal: "gap-2",
      loose: "gap-3",
    },
    preview: {
      true: "items-center gap-12 mx-auto w-fit p-4",
      false: "",
    },
  },
  defaultVariants: {
    layout: "stack",
    columns: 4,
    rows: 1,
    spacing: "normal",
    preview: false,
  },
  compoundVariants: [
    {
      layout: "stack",
      class: "[&>*]:z-0 [&>*:hover]:z-10 w-fit mx-auto",
    },
  ],
});

// Component Logic
const {
  layout = "stack",
  columns = 4,
  rows = 1,
  spacing = "normal",
  class: className = "",
  preview = false,
} = Astro.props as Props;

const containerClasses = twMerge(
  avatarGroupStyles({ layout, columns, rows, spacing, preview }),
  className,
);

// PropTypes for documentation
export const propTypes = {
  layout: {
    type: ["stack", "grid"],
    description: "Layout of the avatar group",
    default: "stack",
  },
  columns: {
    type: "number",
    description: "Number of columns for grid layout",
    default: 4,
  },
  rows: {
    type: "number",
    description: "Number of rows for grid layout",
    default: 1,
  },
  spacing: {
    type: ["tight", "normal", "loose"],
    description: "Spacing between avatars",
    default: "normal",
  },
  borderColor: {
    type: Object.keys(colorPalette),
    description: "Color of the avatar border",
    default: "white",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the avatar group",
  },
  preview: {
    type: "boolean",
    description: "Whether to apply preview styling",
    default: false,
  },
};
---

<div class={containerClasses}>
  <slot />
</div>

Stack with Rounded Avatars

Avatar group with rounded avatars.

 
<div
  class="flex -space-x-4 grid-cols-4 grid-rows-1 gap-2 [&#38;>*]:z-0 [&#38;>*:hover]:z-10 w-fit mx-auto">
  <div class="relative flex items-center justify-center rounded-lg size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-lg size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 1" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-lg size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-lg size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 2" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-lg size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-lg size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 3" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-lg size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-lg size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 4" src="path/to/image.jpg">
    </div>
  </div>
</div>
---
import { AvatarGroup } from '@/components/ui/avatargroup';
import { Avatar } from '@/components/ui/avatar';
import sampleAvatar from '@/images/sample-avatar.jpg';
---

<AvatarGroup>
<Avatar src={sampleAvatar} alt="User 1" shape="rounded" bordered />
<Avatar src={sampleAvatar2} alt="User 2" shape="rounded" bordered />
<Avatar src={sampleAvatar3} alt="User 3" shape="rounded" bordered />
<Avatar src={sampleAvatar4} alt="User 4" shape="rounded" bordered />
</AvatarGroup>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { colorPalette } from "@utils/colorUtils";
import { twMerge } from "tailwind-merge";

// Types and Interfaces
type AvatarGroupVariants = VariantProps<typeof avatarGroupStyles>;

interface Props extends HTMLAttributes<"div">, AvatarGroupVariants {
  layout?: "stack" | "grid";
  columns?: number;
  rows?: number;
  spacing?: "tight" | "normal" | "loose";
  borderColor?: keyof typeof colorPalette;
  class?: string;
  preview?: boolean;
}

// Styles
const avatarGroupStyles = tv({
  base: "flex",
  variants: {
    layout: {
      stack: "-space-x-4",
      grid: "grid w-fit",
    },
    columns: {
      1: "grid-cols-1",
      2: "grid-cols-2",
      3: "grid-cols-3",
      4: "grid-cols-4",
      5: "grid-cols-5",
      6: "grid-cols-6",
    },
    rows: {
      1: "grid-rows-1",
      2: "grid-rows-2",
      3: "grid-rows-3",
      4: "grid-rows-4",
      5: "grid-rows-5",
      6: "grid-rows-6",
    },
    spacing: {
      tight: "gap-1",
      normal: "gap-2",
      loose: "gap-3",
    },
    preview: {
      true: "items-center gap-12 mx-auto w-fit p-4",
      false: "",
    },
  },
  defaultVariants: {
    layout: "stack",
    columns: 4,
    rows: 1,
    spacing: "normal",
    preview: false,
  },
  compoundVariants: [
    {
      layout: "stack",
      class: "[&>*]:z-0 [&>*:hover]:z-10 w-fit mx-auto",
    },
  ],
});

// Component Logic
const {
  layout = "stack",
  columns = 4,
  rows = 1,
  spacing = "normal",
  class: className = "",
  preview = false,
} = Astro.props as Props;

const containerClasses = twMerge(
  avatarGroupStyles({ layout, columns, rows, spacing, preview }),
  className,
);

// PropTypes for documentation
export const propTypes = {
  layout: {
    type: ["stack", "grid"],
    description: "Layout of the avatar group",
    default: "stack",
  },
  columns: {
    type: "number",
    description: "Number of columns for grid layout",
    default: 4,
  },
  rows: {
    type: "number",
    description: "Number of rows for grid layout",
    default: 1,
  },
  spacing: {
    type: ["tight", "normal", "loose"],
    description: "Spacing between avatars",
    default: "normal",
  },
  borderColor: {
    type: Object.keys(colorPalette),
    description: "Color of the avatar border",
    default: "white",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the avatar group",
  },
  preview: {
    type: "boolean",
    description: "Whether to apply preview styling",
    default: false,
  },
};
---

<div class={containerClasses}>
  <slot />
</div>

Stack with Squared Avatars

Avatar group with squared avatars.

 
<div
  class="flex -space-x-4 grid-cols-4 grid-rows-1 gap-2 [&#38;>*]:z-0 [&#38;>*:hover]:z-10 w-fit mx-auto">
  <div class="relative flex items-center justify-center rounded-none size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-none size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 1" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-none size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-none size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 2" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-none size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-none size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 3" src="path/to/image.jpg">
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-none size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-none size-12 text-base overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-2 border-white dark:border-gray-800">
      <img width="400" height="400" alt="User 4" src="path/to/image.jpg">
    </div>
  </div>
</div>
---
import { AvatarGroup } from '@/components/ui/avatargroup';
import { Avatar } from '@/components/ui/avatar';
import sampleAvatar from '@/images/sample-avatar.jpg';
---

<AvatarGroup>
<Avatar src={sampleAvatar} alt="User 1" shape="square" bordered />
<Avatar src={sampleAvatar2} alt="User 2" shape="square" bordered />
<Avatar src={sampleAvatar3} alt="User 3" shape="square" bordered />
<Avatar src={sampleAvatar4} alt="User 4" shape="square" bordered />
</AvatarGroup>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { colorPalette } from "@utils/colorUtils";
import { twMerge } from "tailwind-merge";

// Types and Interfaces
type AvatarGroupVariants = VariantProps<typeof avatarGroupStyles>;

interface Props extends HTMLAttributes<"div">, AvatarGroupVariants {
  layout?: "stack" | "grid";
  columns?: number;
  rows?: number;
  spacing?: "tight" | "normal" | "loose";
  borderColor?: keyof typeof colorPalette;
  class?: string;
  preview?: boolean;
}

// Styles
const avatarGroupStyles = tv({
  base: "flex",
  variants: {
    layout: {
      stack: "-space-x-4",
      grid: "grid w-fit",
    },
    columns: {
      1: "grid-cols-1",
      2: "grid-cols-2",
      3: "grid-cols-3",
      4: "grid-cols-4",
      5: "grid-cols-5",
      6: "grid-cols-6",
    },
    rows: {
      1: "grid-rows-1",
      2: "grid-rows-2",
      3: "grid-rows-3",
      4: "grid-rows-4",
      5: "grid-rows-5",
      6: "grid-rows-6",
    },
    spacing: {
      tight: "gap-1",
      normal: "gap-2",
      loose: "gap-3",
    },
    preview: {
      true: "items-center gap-12 mx-auto w-fit p-4",
      false: "",
    },
  },
  defaultVariants: {
    layout: "stack",
    columns: 4,
    rows: 1,
    spacing: "normal",
    preview: false,
  },
  compoundVariants: [
    {
      layout: "stack",
      class: "[&>*]:z-0 [&>*:hover]:z-10 w-fit mx-auto",
    },
  ],
});

// Component Logic
const {
  layout = "stack",
  columns = 4,
  rows = 1,
  spacing = "normal",
  class: className = "",
  preview = false,
} = Astro.props as Props;

const containerClasses = twMerge(
  avatarGroupStyles({ layout, columns, rows, spacing, preview }),
  className,
);

// PropTypes for documentation
export const propTypes = {
  layout: {
    type: ["stack", "grid"],
    description: "Layout of the avatar group",
    default: "stack",
  },
  columns: {
    type: "number",
    description: "Number of columns for grid layout",
    default: 4,
  },
  rows: {
    type: "number",
    description: "Number of rows for grid layout",
    default: 1,
  },
  spacing: {
    type: ["tight", "normal", "loose"],
    description: "Spacing between avatars",
    default: "normal",
  },
  borderColor: {
    type: Object.keys(colorPalette),
    description: "Color of the avatar border",
    default: "white",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the avatar group",
  },
  preview: {
    type: "boolean",
    description: "Whether to apply preview styling",
    default: false,
  },
};
---

<div class={containerClasses}>
  <slot />
</div>

Stack with Colored Initials Avatars

Avatar group with colored initials avatars.

 
<div
  class="flex -space-x-4 grid-cols-4 grid-rows-1 gap-2 [&#38;>*]:z-0 [&#38;>*:hover]:z-10 w-fit mx-auto">
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-red-500 dark:bg-red-600 text-white dark:text-white hover:text-red-100 dark:hover:text-red-200 border-2 border-white dark:border-gray-800">
      <span class="font-medium">JD</span>
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-blue-500 dark:bg-blue-600 text-white dark:text-white hover:text-blue-100 dark:hover:text-blue-200 border-2 border-white dark:border-gray-800">
      <span class="font-medium">AB</span>
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-green-500 dark:bg-green-600 text-white dark:text-white hover:text-green-100 dark:hover:text-green-200 border-2 border-white dark:border-gray-800">
      <span class="font-medium">CD</span>
    </div>
  </div>
  <div class="relative flex items-center justify-center rounded-full size-12 text-base">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base overflow-hidden bg-yellow-500 dark:bg-yellow-600 text-white dark:text-white hover:text-yellow-100 dark:hover:text-yellow-200 border-2 border-white dark:border-gray-800">
      <span class="font-medium">EF</span>
    </div>
  </div>
</div>
---
import { AvatarGroup } from '@/components/ui/avatargroup';
import { Avatar } from '@/components/ui/avatar';

---

<AvatarGroup>
<Avatar initials="JD" color="red" bordered />
<Avatar initials="AB" color="blue" bordered />
<Avatar initials="CD" color="green" bordered />
<Avatar initials="EF" color="yellow" bordered />
</AvatarGroup>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { colorPalette } from "@utils/colorUtils";
import { twMerge } from "tailwind-merge";

// Types and Interfaces
type AvatarGroupVariants = VariantProps<typeof avatarGroupStyles>;

interface Props extends HTMLAttributes<"div">, AvatarGroupVariants {
  layout?: "stack" | "grid";
  columns?: number;
  rows?: number;
  spacing?: "tight" | "normal" | "loose";
  borderColor?: keyof typeof colorPalette;
  class?: string;
  preview?: boolean;
}

// Styles
const avatarGroupStyles = tv({
  base: "flex",
  variants: {
    layout: {
      stack: "-space-x-4",
      grid: "grid w-fit",
    },
    columns: {
      1: "grid-cols-1",
      2: "grid-cols-2",
      3: "grid-cols-3",
      4: "grid-cols-4",
      5: "grid-cols-5",
      6: "grid-cols-6",
    },
    rows: {
      1: "grid-rows-1",
      2: "grid-rows-2",
      3: "grid-rows-3",
      4: "grid-rows-4",
      5: "grid-rows-5",
      6: "grid-rows-6",
    },
    spacing: {
      tight: "gap-1",
      normal: "gap-2",
      loose: "gap-3",
    },
    preview: {
      true: "items-center gap-12 mx-auto w-fit p-4",
      false: "",
    },
  },
  defaultVariants: {
    layout: "stack",
    columns: 4,
    rows: 1,
    spacing: "normal",
    preview: false,
  },
  compoundVariants: [
    {
      layout: "stack",
      class: "[&>*]:z-0 [&>*:hover]:z-10 w-fit mx-auto",
    },
  ],
});

// Component Logic
const {
  layout = "stack",
  columns = 4,
  rows = 1,
  spacing = "normal",
  class: className = "",
  preview = false,
} = Astro.props as Props;

const containerClasses = twMerge(
  avatarGroupStyles({ layout, columns, rows, spacing, preview }),
  className,
);

// PropTypes for documentation
export const propTypes = {
  layout: {
    type: ["stack", "grid"],
    description: "Layout of the avatar group",
    default: "stack",
  },
  columns: {
    type: "number",
    description: "Number of columns for grid layout",
    default: 4,
  },
  rows: {
    type: "number",
    description: "Number of rows for grid layout",
    default: 1,
  },
  spacing: {
    type: ["tight", "normal", "loose"],
    description: "Spacing between avatars",
    default: "normal",
  },
  borderColor: {
    type: Object.keys(colorPalette),
    description: "Color of the avatar border",
    default: "white",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the avatar group",
  },
  preview: {
    type: "boolean",
    description: "Whether to apply preview styling",
    default: false,
  },
};
---

<div class={containerClasses}>
  <slot />
</div>

Stack with Shadow Avatars

Avatar group with shadow effect on avatars.

 
<div
  class="flex -space-x-4 grid-cols-4 grid-rows-1 gap-2 [&#38;>*]:z-0 [&#38;>*:hover]:z-10 w-fit mx-auto">
  <div
    class="relative flex items-center justify-center rounded-full size-12 text-base shadow-sm dark:shadow-gray-700/50">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base shadow-sm dark:shadow-gray-700/50 overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
      <img width="400" height="400" alt="User 1" src="path/to/image.jpg">
    </div>
  </div>
  <div
    class="relative flex items-center justify-center rounded-full size-12 text-base shadow-md dark:shadow-gray-700/50">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base shadow-md dark:shadow-gray-700/50 overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
      <img width="400" height="400" alt="User 2" src="path/to/image.jpg">
    </div>
  </div>
  <div
    class="relative flex items-center justify-center rounded-full size-12 text-base shadow-lg dark:shadow-gray-600/50">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base shadow-lg dark:shadow-gray-600/50 overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
      <img width="400" height="400" alt="User 3" src="path/to/image.jpg">
    </div>
  </div>
  <div
    class="relative flex items-center justify-center rounded-full size-12 text-base shadow-xl dark:shadow-gray-500/50">
    <div
      class="relative flex items-center justify-center rounded-full size-12 text-base shadow-xl dark:shadow-gray-500/50 overflow-hidden bg-gray-500 dark:bg-gray-600 text-white dark:text-white hover:text-gray-100 dark:hover:text-gray-200 border-gray-600 dark:border-gray-500">
      <img width="400" height="400" alt="User 4" src="path/to/image.jpg">
    </div>
  </div>
</div>
---
import { AvatarGroup } from '@/components/ui/avatargroup';
import { Avatar } from '@/components/ui/avatar';
import sampleAvatar from '@/images/sample-avatar.jpg';
---

<AvatarGroup>
<Avatar src={sampleAvatar} alt="User 1" shadow="sm" />
<Avatar src={sampleAvatar2} alt="User 2" shadow="md" />
<Avatar src={sampleAvatar3} alt="User 3" shadow="lg" />
<Avatar src={sampleAvatar4} alt="User 4" shadow="xl" />
</AvatarGroup>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { colorPalette } from "@utils/colorUtils";
import { twMerge } from "tailwind-merge";

// Types and Interfaces
type AvatarGroupVariants = VariantProps<typeof avatarGroupStyles>;

interface Props extends HTMLAttributes<"div">, AvatarGroupVariants {
  layout?: "stack" | "grid";
  columns?: number;
  rows?: number;
  spacing?: "tight" | "normal" | "loose";
  borderColor?: keyof typeof colorPalette;
  class?: string;
  preview?: boolean;
}

// Styles
const avatarGroupStyles = tv({
  base: "flex",
  variants: {
    layout: {
      stack: "-space-x-4",
      grid: "grid w-fit",
    },
    columns: {
      1: "grid-cols-1",
      2: "grid-cols-2",
      3: "grid-cols-3",
      4: "grid-cols-4",
      5: "grid-cols-5",
      6: "grid-cols-6",
    },
    rows: {
      1: "grid-rows-1",
      2: "grid-rows-2",
      3: "grid-rows-3",
      4: "grid-rows-4",
      5: "grid-rows-5",
      6: "grid-rows-6",
    },
    spacing: {
      tight: "gap-1",
      normal: "gap-2",
      loose: "gap-3",
    },
    preview: {
      true: "items-center gap-12 mx-auto w-fit p-4",
      false: "",
    },
  },
  defaultVariants: {
    layout: "stack",
    columns: 4,
    rows: 1,
    spacing: "normal",
    preview: false,
  },
  compoundVariants: [
    {
      layout: "stack",
      class: "[&>*]:z-0 [&>*:hover]:z-10 w-fit mx-auto",
    },
  ],
});

// Component Logic
const {
  layout = "stack",
  columns = 4,
  rows = 1,
  spacing = "normal",
  class: className = "",
  preview = false,
} = Astro.props as Props;

const containerClasses = twMerge(
  avatarGroupStyles({ layout, columns, rows, spacing, preview }),
  className,
);

// PropTypes for documentation
export const propTypes = {
  layout: {
    type: ["stack", "grid"],
    description: "Layout of the avatar group",
    default: "stack",
  },
  columns: {
    type: "number",
    description: "Number of columns for grid layout",
    default: 4,
  },
  rows: {
    type: "number",
    description: "Number of rows for grid layout",
    default: 1,
  },
  spacing: {
    type: ["tight", "normal", "loose"],
    description: "Spacing between avatars",
    default: "normal",
  },
  borderColor: {
    type: Object.keys(colorPalette),
    description: "Color of the avatar border",
    default: "white",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the avatar group",
  },
  preview: {
    type: "boolean",
    description: "Whether to apply preview styling",
    default: false,
  },
};
---

<div class={containerClasses}>
  <slot />
</div>

Component Properties

Property Type Default Description
layout stack | grid "stack" Layout of the avatar group
columns number 4 Number of columns for grid layout
rows number 1 Number of rows for grid layout
spacing tight | normal | loose "normal" Spacing between avatars
borderColor white | slate | gray | zinc | neutral | stone | red | orange | amber | yellow | lime | green | emerald | teal | cyan | sky | blue | indigo | violet | purple | fuchsia | pink | rose "white" Color of the avatar border
class string - Additional CSS classes to apply to the avatar group
preview boolean false Whether to apply preview styling