oriUI

Avatar

A data-display component that shows an image, or initials derived from text when there is no image (or while it loads). Supports a title / subtitle column for list and profile UIs.

The examples are organised by layer: the class reference is the standalone @oriui/css layer, and the Framework API is the @oriui/vue component. Every example is live — flip its code between HTML (the standalone classes, also your htmx / Astro / Svelte / plain-HTML usage), Vue; HTML is the default.

Classes

An avatar is a block class plus single-class token utilities — one class repoints one token; no separate base class is needed. The Vue props in Framework API mirror these — font size and the titled layout are derived from size and title / subtitle. There is no variant; an optional color tints the initials backdrop.

ClassTypeDescription
ori-avatarBlockRequired base class.
ori-color_*Colorprimary · secondary · success · warn · danger · info · surface · background — tints the initials backdrop
ori-avatar_* (size)Sizexs · sm · md · lg · xl · xxl
ori-size-action-space_*Sizeadds margin around the avatar when spaced is set
ori-size-radius_*Radiuszero · xs · sm · md · lg · xl · rounded
ori-font-size_*Fontscales the initials text with the avatar size
ori-avatar__image · ori-avatar__backdrop · ori-avatar__text · ori-avatar__title · ori-avatar__subtitlePartimage / initials fallback / text column / title / subtitle
ori-avatar_inline · ori-avatar_titled · ori-avatar_reverseLayoutinline flow · title+subtitle layout · reversed image/text order

Image & initials

src (and any other image attribute) falls through via $attrs to the <img> element. When no src is present — or while the image loads — the initials backdrop is shown instead, computing up to two letters from the first two words of text.

html
<!-- with image — bare .ori-avatar is already lg + rounded (defaults baked in) -->
<div class="ori-avatar">
    <img class="ori-avatar__image" src="/portrait.jpg" alt="Marcus Tullius Cicero" />
</div>

<!-- initials fallback: backdrop is aria-hidden -->
<div class="ori-avatar">
    <div class="ori-avatar__backdrop" aria-hidden="true">MT</div>
</div>
vue
<OriAvatar src="/portrait.jpg" text="Marcus Tullius Cicero" />
<!-- no src — initials "MT" are derived from text -->
<OriAvatar text="Marcus Tullius Cicero" />
<!-- two words — initials "AL" -->
<OriAvatar text="Ada Lovelace" />

Colors

An optional color tints the initials backdrop using the standard semantic token. Has no effect when an image is showing.

html
<div class="ori-avatar ori-color_danger">
    <div class="ori-avatar__backdrop" aria-hidden="true">D</div>
</div>
vue
<OriAvatar text="Primary" color="primary" />
<OriAvatar text="Danger" color="danger" />
<OriAvatar text="Info" color="info" />
<OriAvatar text="Surface" color="surface" />
<OriAvatar text="Background" color="background" />

Sizes

xsxxl. The size drives the box dimensions (ori-avatar_*) and the initials scale (ori-font-size_*). Default is lg.

html
<div class="ori-avatar ori-avatar_sm ori-font-size_sm">
    <div class="ori-avatar__backdrop" aria-hidden="true">S</div>
</div>
<div class="ori-avatar ori-avatar_xl ori-font-size_xl">
    <div class="ori-avatar__backdrop" aria-hidden="true">EL</div>
</div>
vue
<OriAvatar text="Small" size="sm" />
<OriAvatar text="Large" size="lg" />
<OriAvatar text="Extra Large" size="xl" />

Radius

From zero (square) to the default rounded (full pill / circle).

html
<div class="ori-avatar ori-size-radius_zero">
    <div class="ori-avatar__backdrop" aria-hidden="true">Z</div>
</div>
vue
<OriAvatar text="Zero" radius="zero" />
<OriAvatar text="Rounded" radius="rounded" />

With title & subtitle

Pass title and/or subtitle to show a text column beside the image or initials. The root element switches to max-content width via ori-avatar_titled.

Marcus Tullius Cicero
statesman · lawyer · writer · orator
Ada Lovelace
mathematician · first programmer
html
<!-- ori-avatar_titled activates the text column layout -->
<div class="ori-avatar ori-avatar_titled">
    <img class="ori-avatar__image" src="/portrait.jpg" alt="Marcus Tullius Cicero" />
    <div class="ori-avatar__text">
        <div class="ori-avatar__title">Marcus Tullius Cicero</div>
        <div class="ori-avatar__subtitle">statesman · lawyer · writer · orator</div>
    </div>
</div>
vue
<OriAvatar
    src="/portrait.jpg"
    text="Marcus Tullius Cicero"
    title="Marcus Tullius Cicero"
    subtitle="statesman · lawyer · writer · orator"
/>
<!-- initials with title -->
<OriAvatar text="Ada Lovelace" color="secondary" title="Ada Lovelace" subtitle="mathematician · first programmer" />

Reversed

reverse flips the layout so the text column appears before the image.

Ada Lovelace
mathematician
html
<div class="ori-avatar ori-avatar_titled ori-avatar_reverse ori-color_primary">
    <div class="ori-avatar__text">
        <div class="ori-avatar__title">Ada Lovelace</div>
        <div class="ori-avatar__subtitle">mathematician</div>
    </div>
    <div class="ori-avatar__backdrop" aria-hidden="true">AL</div>
</div>
vue
<OriAvatar text="Ada Lovelace" color="primary" title="Ada Lovelace" subtitle="mathematician" reverse />

Inline

inline renders the avatar as inline-flex with a small margin, so it flows inside a sentence or a tag list.

html
<div class="ori-avatar ori-avatar_inline ori-avatar_xs ori-font-size_xs">
    <div class="ori-avatar__backdrop" aria-hidden="true">AL</div>
</div>
vue
<OriAvatar text="Ada Lovelace" size="xs" inline />
<OriAvatar text="Marcus Tullius" size="xs" color="secondary" inline />

Spaced

spaced adds padding around the avatar via ori-size-action-space, useful when the avatar sits inside a container that provides no gap of its own.

html
<div class="ori-avatar ori-size-action-space_lg">
    <div class="ori-avatar__backdrop" aria-hidden="true">AL</div>
</div>
vue
<OriAvatar text="Ada Lovelace" spaced />
<OriAvatar text="Marcus Tullius" color="secondary" spaced />

Common patterns

A user-list row and a comment header — the everyday compositions.

Marcus Tullius Cicero
Admin
Ada Lovelace
Editor
Unknown User
Guest
html
<ul style="display: flex; flex-direction: column; gap: 0.75rem; list-style: none; padding: 0">
    <li>
        <div class="ori-avatar ori-avatar_titled">
            <img class="ori-avatar__image" src="/portrait.jpg" alt="Marcus Tullius Cicero" />
            <div class="ori-avatar__text">
                <div class="ori-avatar__title">Marcus Tullius Cicero</div>
                <div class="ori-avatar__subtitle">Admin</div>
            </div>
        </div>
    </li>
</ul>
vue
<!-- user list row -->
<ul style="display: flex; flex-direction: column; gap: 0.75rem; list-style: none; padding: 0">
    <li v-for="user in users" :key="user.id">
        <OriAvatar
            :src="user.avatar"
            :text="user.name"
            :title="user.name"
            :subtitle="user.role"
        />
    </li>
</ul>

Accessibility

The accessibility contract holds across every layer — the standalone classes and the Vue component render the same attributes.

  • The <img> alt is always set — to the text prop value, or '' (empty, decorative) when text is omitted. Do not pass a separate alt via $attrs; it will be overridden.
  • The initials backdrop is aria-hidden="true" — it is a visual fallback, not semantic content.
  • The <img> is revealed only after the load event fires (v-show), preventing a flash of a broken-image icon while the initials are still visible.
  • There is no interactive behaviour; the component has no keyboard contract of its own. Wrap it in a <button> or <a> when it needs to be activatable, and supply an aria-label on the wrapper.
AttributeElementNotes
alt (from text)<img>Set automatically; omit alt in $attrs — it will be replaced.
aria-hidden="true"__backdropInitials are decorative; screen readers read the image alt.

Framework API

The props, events, and slots of the Vue component. The standalone CSS layer has no component API — its surface is the classes above. (Svelte bindings are planned.)

Props

PropTypeDefaultDescription
colorThemeColorTints the initials backdrop: primary · secondary · success · warn · danger · info · surface · background.
inlinebooleanfalseRenders as inline-flex with a small margin for flowing inside text.
radiusRadiusSize'rounded'Corner radius (zero · xs · sm · md · lg · xl · rounded).
reversebooleanfalseReverses the flex direction so the text column appears before the image.
sizeActionSize'lg'Box size and initials scale (xs · sm · md · lg · xl · xxl).
spacedbooleanfalseAdds padding around the avatar via ori-size-action-space.
subtitlestringSecondary line in the text column; rendered whenever title or subtitle is set.
textstringDrives the initials (up to two letters from the first two words) and the image alt.
titlestringPrimary line in the text column; the text column appears when either title or subtitle is set.

Events & attributes

OriAvatar declares no custom events. It sets inheritAttrs: false and applies v-bind="$attrs" directly to the <img> element — so src, alt, width, height, loading, decoding, and any other image attribute or listener fall through to the image, not the wrapper <div>. Native event listeners (@error, @load, …) follow the same path.

The alt attribute is overridden internally to the value of text (or '' when text is not set) so there is a single source of truth for the accessible name.

Slots

Each slot overrides the content it names; leave it out and the component falls back to the derived value or prop. The image itself is not slotted — it comes through $attrs (see above).

SlotFalls back toDescription
fallbackthe initials derived from textImageless fallback content — e.g. a person icon or monogram — shown when there is no image or while it loads.
titletitle propPrimary line in the text column.
subtitlesubtitle propSecondary line in the text column.

The text column appears when any of title / subtitle (prop or slot) is set. The #fallback slot fills the aria-hidden backdrop, so keep its content decorative — the accessible name still comes from the image alt.