Theme
Version
GitHub PyPI Discord
On this page

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

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.

Compare image and fallback paths
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.

Compare named and decorative Avatars
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.

Compare Avatar variants and sizes
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
Compare Avatar shapes
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.

Change an Avatar source
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.

Compose Avatar with badges and groups
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.

Customize Avatar with public CSS
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(...).

InputTypeDefaultEffect
srcstr | NoneNoneSets one escaped image URL. None shows the fallback only.
altstr""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)NoneAdds root classes and merges them with attrs.
stylestr | Mapping[str, str | int | float | bool | None] | Sequence[CStyleValue] | None (CStyleValue)NoneAdds root inline styles and merges them with attrs.
attrsMapping[str, object] | NoneNoneAdds copied trusted root attributes without replacing Avatar semantics, focus, children, reflections, or Citry runtime fields.
img_attrsMapping[str, object] | NoneNoneAdds 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 />.

InputTypeOmitted behaviorEffect
srcstring | nullUses the server input.Replaces the current image URL or switches to fallback-only output.
altstringUses 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

SlotRequiredDataFallback
defaultno{} (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

EventSignatureTrigger and timingDetailControlled and cancellation behavior
onStatusChange(detail: {status: CAvatarStatus, src: string | null}) => voidThe 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.

VariableTypePurposeDefault
--cui-avatar-sizelengthRoot inline and block size.Size-derived 2rem, 2.5rem, or 3rem.
--cui-avatar-backgroundcolorFallback surface.Variant- and scheme-derived color.
--cui-avatar-foregroundcolorFallback text and icon foreground.Variant- and scheme-derived color.
--cui-avatar-border-colorcolorRoot boundary color.Transparent except outline.
--cui-avatar-border-widthlengthRoot boundary width.1px
--cui-avatar-radiuslengthRoot clipping radius.Shape-derived.
--cui-avatar-font-sizelengthAuthored fallback text size.Size-derived.
--cui-avatar-font-weightfont-weightAuthored fallback text emphasis.700
--cui-avatar-image-fitkeywordInternal image object fit.cover
--cui-avatar-image-positionpositionInternal 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

AttributeElementTypeMeaning
data-variantRoot"soft" | "solid" | "outline"Mirrors effective fallback emphasis.
data-sizeRoot"sm" | "md" | "lg"Mirrors effective size.
data-shapeRoot"circle" | "rounded" | "square"Mirrors effective clipping geometry.
data-statusRoot"fallback" | "loading" | "loaded" | "error" (CAvatarStatus)Mirrors the current image lifecycle state.
roleNamed root"img"Exposes the Avatar as one image semantic when alt is nonempty.
aria-labelNamed rootstringUses 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

SelectorElementPurpose
[data-citry-ui-part="avatar"]Root spanStable Avatar surface and attrs destination.
[data-citry-ui-part="fallback"]Decorative fallback wrapperAuthored or generic fallback styling.
[data-citry-ui-part="image"]Decorative imageImage presentation and img_attrs destination.

Interfaces

Aliases and data shapes referenced above.

Input type aliases

InterfaceDefinition
CClassValuestr | Mapping[str, bool] | Sequence[CClassValue]
CStyleValuestr | Mapping[str, str | int | float | bool | None] | Sequence[CStyleValue]
CAvatarVariantLiteral["soft", "solid", "outline"]
CAvatarSizeLiteral["sm", "md", "lg"]
CAvatarShapeLiteral["circle", "rounded", "square"]
CAvatarStatusLiteral["fallback", "loading", "loaded", "error"]

CAvatarDefaultSlotData

Empty dataclass: {}.

Translation keys

-