General
Avatar code
Copy the implementation or inspect every file that belongs to this component unit.
resources/views/components/ui/avatar.blade.php
@props([
'src' => null,
'name' => null,
'size' => 'base',
'shape' => 'circle',
'status' => null,
'icon' => 'user',
'initials' => null,
'background' => null,
'color' => null,
'text' => true,
])
@php
/*
| Avatar image with initials fallback and optional status dot.
|
| size: xs, sm, base, lg, xl, 2xl
| shape: circle, square, rounded
| status: online, away, busy, offline
|
| Customisation:
| initials: override the derived initials (e.g. a user's custom characters)
| background: CSS background value — solid hex, linear-gradient(...), or a
| generated pattern from PaintSwirlGenerator / GlassOrbGenerator /
| IdenticonGenerator / FacetedGemGenerator /
| MeshGradientGenerator / TopographicGenerator /
| User::avatarBackground()
| color: hex color for the initials/icon characters (e.g. #ffffff)
| text: set false to hide the initials and icon entirely so a
| generated background shows on its own. The name is kept as an
| aria-label so the avatar stays announced.
|
| Identicon canvases are light, so pair them with a dark character color, or
| pass :text="false" to show the mark on its own.
|
| Usage:
| <x-ui.avatar name="Ada Lovelace" status="online" />
| <x-ui.avatar src="/img/u.jpg" size="lg" />
| <x-ui.avatar name="Ada Lovelace" initials="AL" background="#4f46e5" color="#ffffff" />
| <x-ui.avatar name="Ada Lovelace" background="linear-gradient(to bottom right, #4f46e5, #ec4899)" />
| <x-ui.avatar name="Ada Lovelace" shape="square" :background="\App\Support\GlassOrbGenerator::cssBackground(['#0ea5e9', '#1d4ed8'], 88)" />
| <x-ui.avatar name="Ada Lovelace" shape="rounded" :background="\App\Support\GlassOrbGenerator::cssBackground(['#0ea5e9', '#1d4ed8'], 88)" />
| <x-ui.avatar name="Ada Lovelace" :background="\App\Support\GlassOrbGenerator::cssBackground(['#0ea5e9', '#1d4ed8'], 88)" />
| <x-ui.avatar name="Ada Lovelace" :background="\App\Support\IdenticonGenerator::cssBackground(['#0ea5e9', '#f59e0b'], 4242)" color="#0f172a" />
| <x-ui.avatar name="Ada Lovelace" :text="false" :background="\App\Support\IdenticonGenerator::cssBackground(['#0ea5e9', '#f59e0b'], 4242)" />
| <x-ui.avatar name="Ada Lovelace" :background="\App\Support\FacetedGemGenerator::cssBackground(['#38bdf8', '#2563eb', '#0f172a'], 42)" />
| <x-ui.avatar name="Ada Lovelace" :background="\App\Support\MeshGradientGenerator::cssBackground(['#f472b6', '#a855f7', '#38bdf8'], 128)" />
| <x-ui.avatar name="Ada Lovelace" :background="\App\Support\TopographicGenerator::cssBackground(['#052e16', '#15803d', '#0891b2'], 314)" />
| <x-ui.avatar name="Ada Lovelace" :background="\App\Support\GlassOrbGenerator::cssBackground(
| ['#0ea5e9', '#1d4ed8'],
| 88,
| ['highlight' => ['colors' => ['#ffffff', '#67e8f9']], 'shadow' => ['colors' => ['#020617', '#312e81']]],
| )" />
| <x-ui.avatar name="Ada Lovelace" :background="$user->avatarBackground()" :color="$user->avatar_character_color" />
*/
$sizes = [
'xs' => 'w-6 h-6 text-[10px]',
'sm' => 'w-8 h-8 text-xs',
'base' => 'w-10 h-10 text-sm',
'lg' => 'w-12 h-12 text-base',
'xl' => 'w-16 h-16 text-lg',
'2xl' => 'w-20 h-20 text-2xl',
];
$shapes = [
'circle' => 'rounded-full',
'square' => 'rounded-none',
'rounded' => 'rounded-2xl',
];
$statusColors = [
'online' => 'bg-green-500',
'away' => 'bg-amber-500',
'busy' => 'bg-red-500',
'offline' => 'bg-gray-400',
];
$statusSize = in_array($size, ['xs', 'sm']) ? 'w-2 h-2' : 'w-3 h-3';
$showText = (bool) $text;
$resolvedInitials = $showText
? ($initials
?: ($name
? \Illuminate\Support\Str::of($name)->explode(' ')->take(2)->map(fn ($p) => mb_substr($p, 0, 1))->implode('')
: null))
: null;
// With the characters hidden there is no text for assistive tech to read, so
// the name moves onto the wrapper as a label. With no name at all the avatar
// is purely decorative and is hidden instead.
$silent = ! $showText && ! $src;
$accessibilityAttributes = $silent
? (filled($name) ? ['role' => 'img', 'aria-label' => $name] : ['aria-hidden' => 'true'])
: [];
$sizeClass = $sizes[$size] ?? $sizes['base'];
$shapeClass = $shapes[$shape] ?? $shapes['circle'];
$isCircular = ($shapes[$shape] ?? $shapes['circle']) === $shapes['circle'];
// Custom colors are arbitrary hex values, so apply them inline. Fall back to
// the brand gradient/white text when no custom colors are provided.
$hasCustomBackground = filled($background);
// Circular generators (glass orbs) leave transparent corners in their SVG.
// For square/card shapes, zoom the background so the marble fills the tile.
$resolvedBackground = $background;
if ($hasCustomBackground && ! $isCircular && is_string($background) && str_contains($background, 'center/cover')) {
$resolvedBackground = str_replace('center/cover no-repeat', 'center / 145% 145% no-repeat', $background);
}
$bubbleClasses = $hasCustomBackground
? 'inline-flex items-center justify-center overflow-hidden font-semibold ring-2 ring-white dark:ring-gray-900 '.$shapeClass.' '.$sizeClass
: 'inline-flex items-center justify-center overflow-hidden bg-gradient-to-br from-brand-500 to-brand-700 font-semibold text-white ring-2 ring-white dark:ring-gray-900 '.$shapeClass.' '.$sizeClass;
$bubbleStyles = collect([
$hasCustomBackground ? "background:{$resolvedBackground}" : null,
filled($color) ? "color:{$color}" : null,
])->filter()->implode(';');
@endphp
<span {{ $attributes->merge(['class' => 'relative inline-flex shrink-0'] + $accessibilityAttributes) }}>
<span class="{{ $bubbleClasses }}" @if ($bubbleStyles) style="{{ $bubbleStyles }}" @endif>
@if ($src)
<img src="{{ $src }}" alt="{{ $name }}" class="h-full w-full object-cover">
@elseif ($resolvedInitials)
{{ $resolvedInitials }}
@elseif ($showText)
<x-ui.icon :name="$icon" weight="fill" size="base" />
@endif
</span>
@if ($status)
<span class="absolute bottom-0 end-0 rounded-full ring-2 ring-white dark:ring-gray-900 {{ $statusSize }} {{ $statusColors[$status] ?? $statusColors['offline'] }}"></span>
@endif
</span>