Appearance
@vibe-labs/design-vue-avatars
Vue 3 avatar components for the Vibe Design System. Wraps @vibe-labs/design-components-avatars with full reactivity, deterministic color generation, LQIP image loading, and accessibility.
Installation
ts
import { VibeAvatar, VibeAvatarGroup, getInitials } from "@vibe-labs/design-vue-avatars";Requires the CSS layer to be loaded (either via @vibe-labs/design-components-avatars or the umbrella @vibe-labs/design-components).
Components
VibeAvatar
Renders a circular avatar with automatic initials fallback, deterministic background colors derived from the name, and optional LQIP image crossfade.
Usage
vue
<!-- Initials only (color auto-generated from name) -->
<VibeAvatar name="Jane Doe" />
<!-- With image -->
<VibeAvatar name="Jane Doe" src="/avatars/jane.jpg" />
<!-- With LQIP thumbnail -->
<VibeAvatar name="Jane Doe" src="/avatars/jane-full.jpg" thumb-src="/avatars/jane-thumb.jpg" />
<!-- With status -->
<VibeAvatar name="Jane Doe" src="/avatars/jane.jpg" status="online" />
<!-- Sizes -->
<VibeAvatar name="Jane Doe" size="xs" />
<VibeAvatar name="Jane Doe" size="sm" />
<VibeAvatar name="Jane Doe" size="md" />
<VibeAvatar name="Jane Doe" size="lg" />
<VibeAvatar name="Jane Doe" size="xl" />
<!-- Custom colors -->
<VibeAvatar name="Jane Doe" bg-color="#ff6b6b" fg-color="#fff" />
<!-- Clickable (adds button role + keyboard support) -->
<VibeAvatar name="Jane Doe" clickable @click="openProfile" />
<!-- With ring -->
<VibeAvatar name="Jane Doe" ring />
<!-- Custom overlay slot -->
<VibeAvatar name="Jane Doe">
<MyBadgeOverlay />
</VibeAvatar>Props
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | required | Full name — used for initials and deterministic color |
initials | string | auto | Override auto-generated initials (max 2 chars) |
src | string | — | Profile image URL |
thumbSrc | string | — | Low-quality placeholder for LQIP crossfade |
alt | string | name | Image alt text |
size | AvatarSize | "md" | xs · sm · md · lg · xl |
status | AvatarStatus | — | online · offline · busy · away |
bgColor | string | auto | Override background color |
fgColor | string | auto | Override text color (auto-contrasted if bgColor set) |
loading | ImageLoadingStrategy | "lazy" | Image loading strategy |
ring | boolean | false | Show outer ring |
clickable | boolean | false | Enable button role + keyboard activation |
Events
| Event | Payload | Description |
|---|---|---|
imageLoad | string (src) | Image loaded successfully |
imageError | string | undefined | Image failed to load |
Slots
| Slot | Description |
|---|---|
default | Custom overlays, badges, or decorations |
How It Works
- Initials are extracted from
name(first letter of each word, max 2) and displayed immediately - Deterministic colors are generated from
nameviaColorFromString— same name always produces the same color - If
srcis provided, VibeImage handles loading with optional LQIP crossfade fromthumbSrc - Once the image loads, it covers the initials layer
- If the image fails, initials remain visible as fallback
VibeAvatarGroup
Horizontal stack of avatars with overlap and optional "+N" overflow indicator.
Usage
vue
<VibeAvatarGroup :max="3" label="Team members">
<VibeAvatar name="Alice" src="/a.jpg" />
<VibeAvatar name="Bob" src="/b.jpg" />
<VibeAvatar name="Charlie" src="/c.jpg" />
<VibeAvatar name="Diana" src="/d.jpg" />
<VibeAvatar name="Eve" src="/e.jpg" />
</VibeAvatarGroup>
<!-- Renders: [Alice] [Bob] [Charlie] +2 -->Props
| Prop | Type | Default | Description |
|---|---|---|---|
max | number | — | Maximum visible avatars before overflow badge |
label | string | — | Accessible group label (aria-label) |
Utilities
getInitials(name, max?)
Extract initials from a name string.
ts
import { getInitials } from "@vibe-labs/design-vue-avatars";
getInitials("John Doe"); // "JD"
getInitials("Alice"); // "A"
getInitials("Jean-Luc Picard"); // "JP"
getInitials("A B C D", 3); // "ABC"Dependencies
| Package | Purpose |
|---|---|
@vibe-labs/core | ColorFromString deterministic color generation |
@vibe-labs/design-components-avatars | CSS tokens + generated styles |
@vibe-labs/design-vue-images | VibeImage component for LQIP loading |
Build
bash
npm run buildBuilt with Vite + vite-plugin-dts. Outputs ES module with TypeScript declarations.
Usage Guide
Setup
ts
import { VibeAvatar, VibeAvatarGroup } from "@vibe-labs/design-vue-avatars";
import "@vibe-labs/design-components-avatars";VibeAvatar
Basic — initials with auto-color
vue
<script setup lang="ts">
import { VibeAvatar } from "@vibe-labs/design-vue-avatars";
</script>
<template>
<!-- Auto-generates initials "JD" and a deterministic color from the name -->
<VibeAvatar name="Jane Doe" />
<VibeAvatar name="Jane Doe" size="lg" status="online" />
</template>Image with LQIP blur-up
vue
<script setup lang="ts">
import { VibeAvatar } from "@vibe-labs/design-vue-avatars";
const props = defineProps<{ user: { name: string; avatar: string; avatarThumb: string } }>();
</script>
<template>
<!-- thumbSrc shows blurred until full src loads, then crossfades -->
<VibeAvatar
:name="props.user.name"
:src="props.user.avatar"
:thumb-src="props.user.avatarThumb"
size="md"
status="online"
/>
</template>Clickable avatar with badge overlay
vue
<script setup lang="ts">
import { VibeAvatar } from "@vibe-labs/design-vue-avatars";
import { VibeBadgeCount } from "@vibe-labs/design-vue-badges";
const emit = defineEmits<{ openProfile: [] }>();
</script>
<template>
<VibeAvatar name="Alice Smith" src="/alice.jpg" clickable ring @click="emit('openProfile')">
<VibeBadgeCount :count="3" />
</VibeAvatar>
</template>VibeAvatarGroup
Overflow group for a team
vue
<script setup lang="ts">
import { VibeAvatar, VibeAvatarGroup } from "@vibe-labs/design-vue-avatars";
const members = [
{ name: "Alice", src: "/alice.jpg" },
{ name: "Bob", src: "/bob.jpg" },
{ name: "Charlie", src: "/charlie.jpg" },
{ name: "Diana", src: "/diana.jpg" },
{ name: "Eve", src: "/eve.jpg" },
];
</script>
<template>
<!-- Shows first 3, then "+2" overflow -->
<VibeAvatarGroup :max="3" label="Team members">
<VibeAvatar
v-for="m in members"
:key="m.name"
:name="m.name"
:src="m.src"
size="sm"
/>
</VibeAvatarGroup>
</template>Common Patterns
Avatar in a user profile row
vue
<script setup lang="ts">
import { VibeAvatar } from "@vibe-labs/design-vue-avatars";
defineProps<{ name: string; role: string; avatar?: string }>();
</script>
<template>
<div style="display: flex; align-items: center; gap: 12px">
<VibeAvatar :name="name" :src="avatar" size="md" />
<div>
<strong>{{ name }}</strong>
<p>{{ role }}</p>
</div>
</div>
</template>Dynamic status indicator
vue
<script setup lang="ts">
import { VibeAvatar } from "@vibe-labs/design-vue-avatars";
import type { AvatarStatus } from "@vibe-labs/design-vue-avatars";
const props = defineProps<{ name: string; isOnline: boolean }>();
const status = computed<AvatarStatus>(() => props.isOnline ? "online" : "offline");
</script>
<template>
<VibeAvatar :name="props.name" :status="status" size="sm" />
</template>