Avatar
Use CAvatar for a compact image identity. Supply an explicit accessible name, then choose an image, authored fallback, or built-in generic silhouette.
Avatar at a glance
Show code
import citry_ui
from citry import Component, citry
citry.register_library(citry_ui)
class AvatarAtAGlance(Component):
template = """
<section class="avatar-guide" aria-labelledby="avatar-guide-title">
<p class="avatar-guide__eyebrow">Moonfen field guide</p>
<h2 id="avatar-guide-title">Night expedition</h2>
<div class="avatar-guide__row">
<div><c-CAvatar alt="Mira Vale">MV</c-CAvatar><span>Mira</span></div>
<div><c-CAvatar alt="Orrin Moss" variant="solid">OM</c-CAvatar><span>Orrin</span></div>
<div><c-CAvatar alt="Unknown guide" variant="outline" /><span>Guide</span></div>
</div>
</section>
"""
css = """
:where(.avatar-guide) {
max-inline-size: 28rem;
padding: 1.25rem;
border: 1px solid light-dark(#a8c7b5, #426151);
border-radius: 0.9rem;
background: light-dark(#f4fbf6, #15241c);
color: CanvasText;
font-family: ui-sans-serif, system-ui, sans-serif;
}
:where(.avatar-guide h2, .avatar-guide p) {
margin: 0;
}
:where(.avatar-guide h2) {
margin-block: 0.2rem 1rem;
font-size: 1.1rem;
}
:where(.avatar-guide__eyebrow) {
color: light-dark(#35624b, #a9d7bc);
font-size: 0.72rem;
font-weight: 750;
letter-spacing: 0.08em;
text-transform: uppercase;
}
:where(.avatar-guide__row) {
display: flex;
gap: 1rem;
}
:where(.avatar-guide__row > div) {
display: grid;
justify-items: center;
gap: 0.35rem;
font-size: 0.8rem;
}
"""
preview = AvatarAtAGlance()
preview # noqa: B018
Choose images and fallbacks
src shows one image. The default slot remains behind it and appears when the source is absent or fails. Without a slot, Avatar uses a generic silhouette.
Show code
import citry_ui
from citry import Component, citry
citry.register_library(citry_ui)
PORTRAIT = (
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 80 80'%3E"
"%3Crect width='80' height='80' fill='%23365f50'/%3E"
"%3Ccircle cx='40' cy='31' r='14' fill='%23f4d6b0'/%3E"
"%3Cpath d='M13 80c4-22 15-32 27-32s23 10 27 32' fill='%238fc5a8'/%3E%3C/svg%3E"
)
class AvatarImages(Component):
class Kwargs:
pass
class Slots:
pass
template = """
<div class="avatar-image-grid">
<div><c-CAvatar c-src="portrait" alt="Fen cartographer">FC</c-CAvatar><span>Loaded</span></div>
<div>
<c-CAvatar src="/missing-moonfen-portrait.png" alt="Marsh scout">MS</c-CAvatar>
<span>Error fallback</span>
</div>
<div><c-CAvatar alt="Unassigned explorer" /><span>Generic fallback</span></div>
</div>
"""
def template_data(
self,
kwargs: Kwargs, # noqa: ARG002
slots: Slots, # noqa: ARG002
) -> dict[str, object]:
return {"portrait": PORTRAIT}
css = """
:where(.avatar-image-grid) {
display: flex;
flex-wrap: wrap;
gap: 1rem;
font-family: ui-sans-serif, system-ui, sans-serif;
}
:where(.avatar-image-grid > div) {
display: grid;
justify-items: center;
gap: 0.35rem;
color: light-dark(#315546, #b7ddc8);
font-size: 0.75rem;
}
"""
preview = AvatarImages()
preview # noqa: B018
<c-CAvatar src="/portraits/mira.jpg" alt="Mira Vale">MV</c-CAvatar>
Python composition uses the same surface:
from citry_ui import CAvatar
avatar = CAvatar(src="/portraits/mira.jpg", alt="Mira Vale")
Provide an accessible name
Use alt for the identity conveyed by Avatar. An empty value is deliberately decorative. The internal image never duplicates the root name.
Show code
import citry_ui
from citry import Component, citry
citry.register_library(citry_ui)
class AvatarNames(Component):
template = """
<div class="avatar-name-list">
<div><c-CAvatar alt="Mira Vale">MV</c-CAvatar><span>Named identity</span></div>
<div><c-CAvatar><span aria-hidden="true">MF</span></c-CAvatar><span>Decorative companion</span></div>
</div>
"""
css = """
:where(.avatar-name-list) {
display: grid;
gap: 0.75rem;
font-family: ui-sans-serif, system-ui, sans-serif;
}
:where(.avatar-name-list > div) {
display: flex;
align-items: center;
gap: 0.75rem;
}
"""
preview = AvatarNames()
preview # noqa: B018
Choose appearance
Variants style the fallback. Sizes and shapes control the fixed visual frame.
Show code
import citry_ui
from citry import Component, citry
citry.register_library(citry_ui)
class AvatarVariants(Component):
class Kwargs:
pass
class Slots:
pass
template = """
<div class="avatar-variants">
<c-for each="variant in variants">
<div>
<strong>{{ variant }}</strong>
<c-for each="size in sizes">
<c-CAvatar c-variant="variant" c-size="size" c-alt="f'{variant} {size} guide'">MF</c-CAvatar>
</c-for>
</div>
</c-for>
</div>
"""
def template_data(
self,
kwargs: Kwargs, # noqa: ARG002
slots: Slots, # noqa: ARG002
) -> dict[str, object]:
return {"variants": ("soft", "solid", "outline"), "sizes": ("sm", "md", "lg")}
css = """
:where(.avatar-variants) {
display: grid;
gap: 0.8rem;
font-family: ui-sans-serif, system-ui, sans-serif;
}
:where(.avatar-variants > div) {
display: flex;
align-items: center;
gap: 0.65rem;
}
:where(.avatar-variants strong) {
inline-size: 4.5rem;
color: light-dark(#315546, #b7ddc8);
font-size: 0.75rem;
text-transform: capitalize;
}
"""
preview = AvatarVariants()
preview # noqa: B018
Show code
import citry_ui
from citry import Component, citry
citry.register_library(citry_ui)
class AvatarShapes(Component):
class Kwargs:
pass
class Slots:
pass
template = """
<div class="avatar-shapes">
<c-for each="shape in shapes">
<div>
<c-CAvatar c-shape="shape" c-alt="f'{shape} spirit guide'" variant="soft">SG</c-CAvatar>
<span>{{ shape }}</span>
</div>
</c-for>
</div>
"""
def template_data(
self,
kwargs: Kwargs, # noqa: ARG002
slots: Slots, # noqa: ARG002
) -> dict[str, object]:
return {"shapes": ("circle", "rounded", "square")}
css = """
:where(.avatar-shapes) {
display: flex;
gap: 1rem;
font-family: ui-sans-serif, system-ui, sans-serif;
}
:where(.avatar-shapes > div) {
display: grid;
justify-items: center;
gap: 0.35rem;
font-size: 0.75rem;
text-transform: capitalize;
}
"""
preview = AvatarShapes()
preview # noqa: B018
Update the image in the browser
Client inputs are passed through $c-props="{...}". src accepts a URL or null; onStatusChange reports fallback, loading, loaded, and error states.
Show code
import citry_ui
from citry import Component, citry
citry.register_library(citry_ui)
class AvatarReactive(Component):
template = """
<div
class="avatar-reactive"
x-data="{source: null, status: 'fallback'}"
>
<c-CAvatar
alt="Moonfen lookout"
$c-props="{src: source, onStatusChange: detail => status = detail.status}"
>ML</c-CAvatar>
<p>Status: <strong x-text="status">fallback</strong></p>
<div class="avatar-reactive__actions">
<c-CButton size="sm" @click="source = '/missing-lookout-a.png'">Try missing image</c-CButton>
<c-CButton size="sm" variant="outline" @click="source = null">Use fallback</c-CButton>
</div>
</div>
"""
css = """
:where(.avatar-reactive) {
display: grid;
justify-items: start;
gap: 0.75rem;
font-family: ui-sans-serif, system-ui, sans-serif;
}
:where(.avatar-reactive p) {
margin: 0;
font-size: 0.8rem;
}
:where(.avatar-reactive__actions) {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
"""
preview = AvatarReactive()
preview # noqa: B018
Compose adjacent UI
Avatar does not own presence, badges, or overlapping groups. Compose those jobs with CBadge, CGroup, and application layout.
Show code
import citry_ui
from citry import Component, citry
citry.register_library(citry_ui)
class AvatarComposition(Component):
template = """
<div class="avatar-party">
<div class="avatar-party__member">
<c-CAvatar alt="Mira Vale">MV</c-CAvatar>
<c-CBadge intent="success" shape="pill">Ready</c-CBadge>
</div>
<div class="avatar-party__group" aria-label="Moonfen expedition party">
<c-CAvatar alt="Orrin Moss">OM</c-CAvatar>
<c-CAvatar alt="Sable Reed" variant="solid">SR</c-CAvatar>
<c-CAvatar alt="Tarin Wisp" variant="outline">TW</c-CAvatar>
</div>
</div>
"""
css = """
:where(.avatar-party) {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 1.5rem;
}
:where(.avatar-party__member) {
display: flex;
align-items: center;
gap: 0.5rem;
}
:where(.avatar-party__group) {
display: flex;
padding-inline-start: 0.5rem;
}
:where(.avatar-party__group [data-citry-ui-part="avatar"]) {
margin-inline-start: -0.5rem;
border-color: Canvas;
border-width: 2px;
}
"""
preview = AvatarComposition()
preview # noqa: B018
Customize Avatar
Override public variables on a scope or instance. Stable selectors target the root, fallback, and image without relying on private classes.
Show code
import citry_ui
from citry import Component, citry
citry.register_library(citry_ui)
class AvatarCustomization(Component):
template = """
<div class="avatar-moonlit">
<c-CAvatar alt="Moonlit ranger" size="lg">MR</c-CAvatar>
<c-CAvatar alt="Reed oracle" size="lg" variant="outline">RO</c-CAvatar>
</div>
"""
css = """
:where(.avatar-moonlit) {
--cui-avatar-background: light-dark(#d9f1e4, #234738);
--cui-avatar-foreground: light-dark(#174b35, #c9f4dd);
--cui-avatar-border-color: light-dark(#4b8a69, #83c9a3);
--cui-avatar-radius: 35% 65% 58% 42%;
display: flex;
gap: 0.75rem;
}
:where(.avatar-moonlit [data-citry-ui-part="fallback"]) {
letter-spacing: 0.06em;
}
"""
preview = AvatarCustomization()
preview # noqa: B018
Accessibility and loading behavior
A nonempty alt makes the root one named image semantic. The internal HTML image and fallback are decorative, avoiding duplicate announcements. Empty alt makes the entire Avatar decorative.
Avatar owns no focus or keyboard behavior. Failed images are hidden after client activation; the fallback remains mounted throughout loading.
API reference
Inputs
CAvatar server inputs
Server inputs are passed in a template through <c-CAvatar ... /> or in Python through CAvatar(...).
| Input | Type | Default | Effect |
|---|---|---|---|
src | str | None | None | Sets one escaped image URL. None shows the fallback only. |
alt | str | "" | Names the Avatar as one image semantic. Empty text makes the Avatar decorative. |
variant | "soft" | "solid" | "outline" (CAvatarVariant) | "soft" | Selects fallback visual emphasis. |
size | "sm" | "md" | "lg" (CAvatarSize) | "md" | Selects the size preset. |
shape | "circle" | "rounded" | "square" (CAvatarShape) | "circle" | Selects clipping geometry. |
class_ | str | Mapping[str, bool] | Sequence[CClassValue] | None (CClassValue) | None | Adds root classes and merges them with attrs. |
style | str | Mapping[str, str | int | float | bool | None] | Sequence[CStyleValue] | None (CStyleValue) | None | Adds root inline styles and merges them with attrs. |
attrs | Mapping[str, object] | None | None | Adds copied trusted root attributes without replacing Avatar semantics, focus, children, reflections, or Citry runtime fields. |
img_attrs | Mapping[str, object] | None | None | Adds copied inert image attributes such as loading, decoding, and referrerpolicy without replacing source, alternative text, events, or ownership. |
CAvatar client inputs
Client inputs are passed in the browser through the $c-props="{ ... }" attribute on <c-CAvatar />.
| Input | Type | Omitted behavior | Effect |
|---|---|---|---|
src | string | null | Uses the server input. | Replaces the current image URL or switches to fallback-only output. |
alt | string | Uses the server input. | Updates the root accessible name; empty text makes it decorative. |
variant | "soft" | "solid" | "outline" (CAvatarVariant) | Uses the server input. | Controls fallback visual emphasis. |
size | "sm" | "md" | "lg" (CAvatarSize) | Uses the server input. | Controls size. |
shape | "circle" | "rounded" | "square" (CAvatarShape) | Uses the server input. | Controls clipping geometry. |
Slots
Slots are passed as nested content or <c-fill> tags in a template, or through the slots={...} argument in Python.
CAvatar slots
| Slot | Required | Data | Fallback |
|---|---|---|---|
default | no | {} (CAvatarDefaultSlotData) | Generic decorative person silhouette. |
Events
Component events are callback inputs supplied through $c-props. Native browser events remain available through Alpine @... attributes.
CAvatar events
| Event | Signature | Trigger and timing | Detail | Controlled and cancellation behavior |
|---|---|---|---|---|
onStatusChange | (detail: {status: CAvatarStatus, src: string | null}) => void | The committed image status changes. | {status: "fallback" | "loading" | "loaded" | "error", src: string | null} | Runs after the image visibility and root status reflection synchronize; return values do not cancel the transition. |
Methods
-
CSS
CSS variables to theme the components. Set them on an ancestor or the component itself.
CAvatar CSS variables
Apply these variables to CAvatar or one of its ancestors.
| Variable | Type | Purpose | Default |
|---|---|---|---|
--cui-avatar-size | length | Root inline and block size. | Size-derived 2rem, 2.5rem, or 3rem. |
--cui-avatar-background | color | Fallback surface. | Variant- and scheme-derived color. |
--cui-avatar-foreground | color | Fallback text and icon foreground. | Variant- and scheme-derived color. |
--cui-avatar-border-color | color | Root boundary color. | Transparent except outline. |
--cui-avatar-border-width | length | Root boundary width. | 1px |
--cui-avatar-radius | length | Root clipping radius. | Shape-derived. |
--cui-avatar-font-size | length | Authored fallback text size. | Size-derived. |
--cui-avatar-font-weight | font-weight | Authored fallback text emphasis. | 700 |
--cui-avatar-image-fit | keyword | Internal image object fit. | cover |
--cui-avatar-image-position | position | Internal image object position. | center |
Attributes
HTML attributes defined on the components that you can refer to for CSS, inspection, and testing. Read-only.
CAvatar attributes
| Attribute | Element | Type | Meaning |
|---|---|---|---|
data-variant | Root | "soft" | "solid" | "outline" | Mirrors effective fallback emphasis. |
data-size | Root | "sm" | "md" | "lg" | Mirrors effective size. |
data-shape | Root | "circle" | "rounded" | "square" | Mirrors effective clipping geometry. |
data-status | Root | "fallback" | "loading" | "loaded" | "error" (CAvatarStatus) | Mirrors the current image lifecycle state. |
role | Named root | "img" | Exposes the Avatar as one image semantic when alt is nonempty. |
aria-label | Named root | string | Uses the exact nonempty alt input as the Avatar name. |
Selectors
Selectors for the DOM nodes in the components that you can use for CSS, inspection, and testing.
CAvatar selectors
| Selector | Element | Purpose |
|---|---|---|
[data-citry-ui-part="avatar"] | Root span | Stable Avatar surface and attrs destination. |
[data-citry-ui-part="fallback"] | Decorative fallback wrapper | Authored or generic fallback styling. |
[data-citry-ui-part="image"] | Decorative image | Image presentation and img_attrs destination. |
Interfaces
Aliases and data shapes referenced above.
Input type aliases
| Interface | Definition |
|---|---|
CClassValue | str | Mapping[str, bool] | Sequence[CClassValue] |
CStyleValue | str | Mapping[str, str | int | float | bool | None] | Sequence[CStyleValue] |
CAvatarVariant | Literal["soft", "solid", "outline"] |
CAvatarSize | Literal["sm", "md", "lg"] |
CAvatarShape | Literal["circle", "rounded", "square"] |
CAvatarStatus | Literal["fallback", "loading", "loaded", "error"] |
CAvatarDefaultSlotData
Empty dataclass: {}.
Translation keys
-