User Identity
A consistent user identity row with avatar fallback, display name, secondary identifier, and optional role and presence status in compact or full form.
Preview
<x-ui.user-identity name="Avery Stone" secondary="avery@example.com" role="Owner" status="online" />
Installation
php artisan ui:add user-identity
Registry contract
php artisan ui:add user-identity
writes only the files below. The CLI validates each file hash before writing and asks before it replaces a local change, unless you pass --force.
-
resources/views/components/ui/user-identity.blade.php
Use with AI
A brief for your coding agent: install command, usage, props, guidance and the rules. Copy it, or open a prompt about this component in an assistant.
# Brok UI: User Identity (`user-identity`)
A consistent user identity row with avatar fallback, display name, secondary identifier, and optional role and presence status in compact or full form.
Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.
## Install
```bash
php artisan ui:add user-identity
```
## Usage
```blade
<x-ui.user-identity name="Avery Stone" secondary="[email protected]" role="Owner" status="online" />
```
## Props
- `name` (string, default `required`) — Visible display name.
- `secondary` (string|null, default `null`) — Email, handle, or another secondary identifier.
- `src` (url|null, default `null`) — Validated image URL.
- `fallback` (string|null, default `null`) — Explicit avatar fallback; initials are derived when absent.
- `role` (string|null, default `null`) — Optional role label.
- `status` (online|offline|busy|away|neutral|null, default `null`) — Optional visible presence state.
- `mode` (compact|full|card, default `full`) — compact: avatar and name on one line; full: name, role and a secondary line; card: a bordered card with a large avatar, the status in words and an actions slot.
- `href` (url|null, default `null`) — Optional profile link.
## Use when
- Use to summarize, sequence, or present data so users can scan it quickly.
- Show the same user identity contract in tables, lists, audit feeds, and menus.
## Avoid when
- Do not add display-only ornament when the user needs actionable structure or exact comparison instead.
- Use avatar when only an image or initials are needed.
## Anti-patterns
- Adding display ornament without informational value
## Rules
- Use the `<brok:user-identity>` tag (or `<x-ui.user-identity>`) in Blade; do not rewrite the component.
- Prefer the documented props and variants over utility-class overrides; when a utility must win, use the `!` important modifier.
- Use semantic design tokens (`bg-primary`, `text-muted-foreground`), never raw colour utilities.
- Keep the `data-slot` attributes; they are the styling and test hooks.
## Links
- Docs: https://brokui.dev/docs/components/user-identity
- Registry JSON (files, props, contract): https://brokui.dev/r/open/user-identity.json
Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.
Examples
Long Content
<div class="max-w-xs">
<x-ui.user-identity name="{{ __('A deliberately long translated display name for overflow testing') }}" secondary="a-very-long-secondary-identifier@example.test" role="{{ __('Organization administrator') }}" status="online" />
</div>
{{-- compact for a table cell or a mention, full for a list row, card for a profile header. --}}
<div class="flex w-full max-w-md flex-col items-start gap-6">
<x-ui.user-identity name="Morgan Lee" mode="compact" status="away" />
<x-ui.user-identity name="Morgan Lee" secondary="morgan@example.com" role="Editor" status="away" href="#" />
<x-ui.user-identity name="Morgan Lee" secondary="morgan@example.com" role="Editor" status="away" mode="card" class="w-full">
<x-slot:actions>
<x-ui.button variant="outline" size="sm">{{ __('Message') }}</x-ui.button>
<x-ui.button variant="outline" size="sm" icon-label="{{ __('More') }}">
<svg viewBox="0 0 24 24" fill="currentColor" class="size-4"><circle cx="5" cy="12" r="1.75" /><circle cx="12" cy="12" r="1.75" /><circle cx="19" cy="12" r="1.75" /></svg>
</x-ui.button>
</x-slot:actions>
</x-ui.user-identity>
</div>
{{-- Every presence state, and a long RTL name that truncates. --}}
<div class="flex w-full max-w-md flex-col items-start gap-4">
<div class="flex flex-wrap gap-6">
<x-ui.user-identity name="Ava Chen" mode="compact" status="online" />
<x-ui.user-identity name="Ben Ortiz" mode="compact" status="busy" />
<x-ui.user-identity name="Cleo Park" mode="compact" status="away" />
<x-ui.user-identity name="Dev Rao" mode="compact" status="offline" />
</div>
<div dir="rtl" class="w-full">
<x-ui.user-identity name="اسم طويل جداً لاختبار التخطيط والاقتطاع" secondary="identity@example.com" role="Administrator" status="busy" class="max-w-xs" />
</div>
</div>
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| name | string | required | Visible display name. |
| secondary | string | null | null | Email, handle, or another secondary identifier. |
| src | url | null | null | Validated image URL. |
| fallback | string | null | null | Explicit avatar fallback; initials are derived when absent. |
| role | string | null | null | Optional role label. |
| status | online | offline | busy | away | neutral | null | null | Optional visible presence state. |
| mode | compact | full | card | full | compact: avatar and name on one line; full: name, role and a secondary line; card: a bordered card with a large avatar, the status in words and an actions slot. |
| href | url | null | null | Optional profile link. |
Slots
Default Blade slot only.
Data slots
Stable hooks for CSS overrides and browser tests.
Behavior
- Derives a two-letter fallback when explicit initials are absent.
- Declares registry capability flags: a11y, authoredStateFixtures, responsive, rtl, darkMode, localized.
Guidance
Present data for rapid scanning.
Use when
- Use to summarize, sequence, or present data so users can scan it quickly.
- Show the same user identity contract in tables, lists, audit feeds, and menus.
Avoid when
- Do not add display-only ornament when the user needs actionable structure or exact comparison instead.
- Use avatar when only an image or initials are needed.
Use instead
- Table for exact comparison
- Plain text for a single value
Anti-patterns
- Adding display ornament without informational value
- Anatomy
- Theming hooks
Accessibility
- Keyboard
- No component-owned keyboard interaction; native element behavior applies.
- Focus
none
- Role and status use visible text, not color alone.
- Image and fallback labels use the display name.
- Semantic HTML and a stable
data-slotattribute for styling and scripting hooks. - Focus-visible rings use the
ringtoken, so keyboard focus is always visible. - Disabled and invalid states are conveyed to assistive tech, not by color alone.
- Targets WCAG 2.2 AA; verify contrast in light, dark, admin and customer surfaces in the preview.
- Labels go through
__()and layout uses logical properties (ms-*,text-start), so it mirrors underdir="rtl"— flip the preview to RTL to confirm. - Dark mode uses the same semantic tokens under the
darkclass; high contrast follows forced-color system tokens.
Livewire
Livewire can update this component through forwarded wire:* attributes.
Source
The exact, editable file ui:add writes
into your app. Previews render this same code; there are no preview-only components.
@props([
'name',
'secondary' => null,
'src' => null,
'fallback' => null,
'role' => null,
// online | busy | away | offline | neutral — drawn as a dot on the avatar's
// edge (named for assistive tech), never as a second text tag in the row.
'status' => null,
// compact: avatar sm + name on one line. full: avatar md, name + role, a
// secondary line. card: a bordered card with avatar lg and room for actions.
'mode' => 'full',
'href' => null,
])
@php
$mode = in_array($mode, ['compact', 'full', 'card'], true) ? $mode : 'full';
$tag = filled($href) ? 'a' : ($mode === 'card' ? 'div' : 'span');
$status = in_array($status, ['online', 'busy', 'away', 'offline', 'neutral'], true) ? $status : null;
$initials = $fallback ?? collect(preg_split('/\s+/', trim((string) $name)) ?: [])
->filter()
->take(2)
->map(fn ($part) => mb_strtoupper(mb_substr($part, 0, 1)))
->implode('');
$avatarSize = ['compact' => 'sm', 'full' => 'md', 'card' => 'lg'][$mode];
// The presence dot sits on the avatar's bottom-end edge, ringed by the surface.
$dotSize = ['compact' => 'size-2', 'full' => 'size-3', 'card' => 'size-3'][$mode];
$dotColour = [
'online' => 'bg-success',
'busy' => 'bg-destructive',
'away' => 'bg-warning',
'offline' => 'border-2 border-muted-foreground/60 bg-background',
'neutral' => 'bg-muted-foreground/60',
];
$statusLabels = ['online' => __('Online'), 'busy' => __('Busy'), 'away' => __('Away'), 'offline' => __('Offline'), 'neutral' => __('Unknown')];
$rootClasses = match ($mode) {
'card' => 'flex min-w-0 items-center gap-4 rounded-lg border border-border bg-card p-6 text-start text-card-foreground',
default => 'inline-flex min-w-0 items-center gap-3 text-start text-foreground',
};
if ($tag === 'a') {
$rootClasses .= ' rounded-md transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-[length:var(--ring-width)] focus-visible:ring-ring focus-visible:ring-offset-[length:var(--ring-offset-width)] focus-visible:ring-offset-background motion-reduce:transition-none';
$rootClasses .= $mode === 'card' ? ' hover:bg-muted/40' : '';
}
@endphp
<{{ $tag }}
data-slot="user-identity"
data-mode="{{ $mode }}"
@if ($status) data-status="{{ $status }}" @endif
@if ($tag === 'a') href="{{ $href }}" @endif
{{ $attributes->merge(['class' => $rootClasses]) }}
>
<span class="relative inline-flex shrink-0">
<x-ui.avatar :src="$src" :alt="$src ? (string) $name : ''" :fallback="$initials" :size="$avatarSize" />
@if ($status)
<span data-slot="user-identity-status" class="absolute bottom-0 end-0 rounded-full ring-2 ring-background {{ $dotSize }} {{ $dotColour[$status] }}" title="{{ $statusLabels[$status] }}" aria-hidden="true"></span>
<span class="sr-only">{{ $statusLabels[$status] }}</span>
@endif
</span>
<span class="min-w-0 flex-1">
<span class="flex min-w-0 items-center gap-2">
<span class="truncate {{ $mode === 'card' ? 'text-base font-semibold' : 'text-sm font-medium' }}">{{ $name }}</span>
@if (filled($role))
<x-ui.badge variant="outline" size="sm" class="shrink-0">{{ $role }}</x-ui.badge>
@endif
</span>
@if ($mode !== 'compact' && filled($secondary))
<span class="block truncate text-sm text-muted-foreground">{{ $secondary }}</span>
@endif
@if ($mode === 'card' && $status)
<span class="mt-1 block text-xs text-muted-foreground">{{ $statusLabels[$status] }}</span>
@endif
</span>
@isset($actions)
<span class="flex shrink-0 items-center gap-2">{{ $actions }}</span>
@endisset
</{{ $tag }}>
Ownership & lifecycle
Owner, release state, review evidence and adoption for this item.
- Owner
- Platform UI (@JoshJML)
- Current version
-
1.1.2 - Status
- Stable
- License
-
open - Accessibility reviewed
- No review date recorded
- Last breaking change
- No date recorded
- Deprecation
- Not deprecated
- Contract
-
v6 - Foundation
-
≥ 1.0.0