component

Avatar

Displays a user’s profile image, falling back to initials (derived from name) or a generic person icon when no image is provided or the image fails to load. Inherits panel surface props (background, border, radius, shadow, …) so it can blend into any context.

Default

Pass src to render the image. When src is missing or fails to load, the avatar derives initials from name — or renders a user icon when neither is provided.

Ada LovelaceAT
---
import Avatar from "../Avatar.astro";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "md", direction: "row", align: "center" })}>
  <Avatar
    name="Ada Lovelace"
    src="https://i.pravatar.cc/96?u=ada"
    alt="Ada Lovelace"
  />
  <Avatar name="Alan Turing" />
  <Avatar />
</div>

Size

Five sizes (xs, sm, md, lg, xl) matching the button scale — so an avatar and a button of the same size align perfectly.

ALALALALAL
---
import Avatar from "../Avatar.astro";
import { stack } from "@pindoba/styled-system/patterns";
---

<div
  class={stack({
    gap: "md",
    direction: "row",
    align: "center",
    flexWrap: "wrap",
  })}
>
  <Avatar size="xs" name="Ada Lovelace" />
  <Avatar size="sm" name="Ada Lovelace" />
  <Avatar size="md" name="Ada Lovelace" />
  <Avatar size="lg" name="Ada Lovelace" />
  <Avatar size="xl" name="Ada Lovelace" />
</div>

Shape

Circle (default) or square with a rounded corner.

Ada LovelaceAda Lovelace
---
import Avatar from "../Avatar.astro";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "md", direction: "row", align: "center" })}>
  <Avatar
    shape="circle"
    name="Ada Lovelace"
    src="https://i.pravatar.cc/96?u=ada"
  />
  <Avatar
    shape="square"
    name="Ada Lovelace"
    src="https://i.pravatar.cc/96?u=ada"
  />
</div>

Fallback

Without a src, the avatar falls back to up-to-two uppercase initials derived from name. When name is also missing, a generic icon is rendered.

ALA
---
import Avatar from "../Avatar.astro";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "md", direction: "row", align: "center" })}>
  <Avatar name="Ada Lovelace" />
  <Avatar name="A" />
  <Avatar />
</div>

Group

Wrap avatars in a <Group> to get a classic face pile — subsequent avatars overlap, each ringed by the surrounding background so neighbours stay visually distinct. Works for both circle and square shapes and every size.

Ada LovelaceAlan TuringGrace HopperKen Thompson+3
Ada LovelaceAlan TuringGrace Hopper+12
Ada LovelaceAlan TuringGrace Hopper+5
---
import Avatar from "../Avatar.astro";
import { default as AvatarStack } from "@pindoba/astro-group";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "lg", direction: "column" })}>
  <AvatarStack variant="stack">
    <Avatar name="Ada Lovelace" src="https://i.pravatar.cc/96?u=ada" />
    <Avatar name="Alan Turing" src="https://i.pravatar.cc/96?u=alan" />
    <Avatar name="Grace Hopper" src="https://i.pravatar.cc/96?u=grace" />
    <Avatar name="Ken Thompson" src="https://i.pravatar.cc/96?u=ken" />
    <Avatar name="+3" />
  </AvatarStack>

  <AvatarStack variant="stack">
    <Avatar
      size="lg"
      name="Ada Lovelace"
      src="https://i.pravatar.cc/96?u=ada"
    />
    <Avatar
      size="lg"
      name="Alan Turing"
      src="https://i.pravatar.cc/96?u=alan"
    />
    <Avatar
      size="lg"
      name="Grace Hopper"
      src="https://i.pravatar.cc/96?u=grace"
    />
    <Avatar size="lg" name="+12" />
  </AvatarStack>

  <AvatarStack variant="stack">
    <Avatar
      shape="square"
      name="Ada Lovelace"
      src="https://i.pravatar.cc/96?u=ada"
    />
    <Avatar
      shape="square"
      name="Alan Turing"
      src="https://i.pravatar.cc/96?u=alan"
    />
    <Avatar
      shape="square"
      name="Grace Hopper"
      src="https://i.pravatar.cc/96?u=grace"
    />
    <Avatar shape="square" name="+5" />
  </AvatarStack>
</div>

Status indicator

Avatar no longer ships a built-in status prop. To overlay a presence dot — or any other indicator — wrap the avatar in <Attachment> with placement="bottom-end", anchor="corner", and shape="circle", and pass your indicator through the content slot. This works for any element, not just avatars, and lets you use a colored dot, a <Badge>, an icon, or anything else.

AL
AT
GH
KT
Dennis Ritchie
AL
AT
GH
KT
Dennis Ritchie
AL
AT
GH
KT
Dennis Ritchie
AL
AT
GH
KT
Dennis Ritchie
AL
AT
GH
KT
Dennis Ritchie
AL
AT
GH
KT
Dennis Ritchie
AL
AT
GH
KT
Dennis Ritchie
AL
AT
GH
KT
Dennis Ritchie
---
import Avatar from "../Avatar.astro";
import Attachment from "@pindoba/astro-attachment";
import { stack } from "@pindoba/styled-system/patterns";
import { css } from "@pindoba/styled-system/css";

type Feedback = "success" | "warning" | "danger" | "neutral" | "primary";
type Size = "xs" | "sm" | "md" | "lg";

const sizes = ["xs", "sm", "md", "lg"] as const;

const dotBase = css({
  display: "inline-block",
  borderRadius: "full",
  background: "colorPalette",
  outline: "2px solid",
  outlineColor: "colorPalette.border.bold",
});

const sizeClass: Record<Size, string> = {
  xs: css({ width: "0.5rem", height: "0.5rem" }),
  sm: css({ width: "0.625rem", height: "0.625rem" }),
  md: css({ width: "0.75rem", height: "0.75rem" }),
  lg: css({ width: "0.875rem", height: "0.875rem" }),
};

const feedbackClass: Record<Feedback, string> = {
  primary: css({ colorPalette: "primary" }),
  success: css({ colorPalette: "success" }),
  warning: css({ colorPalette: "warning" }),
  danger: css({ colorPalette: "danger" }),
  neutral: css({ colorPalette: "neutral" }),
};

const dotClass = (size: Size, feedback: Feedback) =>
  `${dotBase} ${sizeClass[size]} ${feedbackClass[feedback]}`;

const people: Array<{ name: string; feedback: Feedback; src?: string }> = [
  { name: "Ada Lovelace", feedback: "success" },
  { name: "Alan Turing", feedback: "warning" },
  { name: "Grace Hopper", feedback: "danger" },
  { name: "Ken Thompson", feedback: "neutral" },
  {
    name: "Dennis Ritchie",
    feedback: "primary",
    src: "https://i.pravatar.cc/96?u=dennis",
  },
];
---

<div class={stack({ gap: "lg", direction: "column" })}>
  {
    sizes.map((size) => (
      <div
        class={stack({
          gap: "md",
          direction: "row",
          align: "center",
          flexWrap: "wrap",
        })}
      >
        {people.map((person) => (
          <Attachment placement="bottom-end" anchor="corner" shape="circle">
            <Avatar size={size} name={person.name} src={person.src} />
            <span slot="content" class={dotClass(size, person.feedback)} />
          </Attachment>
        ))}
      </div>
    ))
  }
  {
    sizes.map((size) => (
      <div
        class={stack({
          gap: "md",
          direction: "row",
          align: "center",
          flexWrap: "wrap",
        })}
      >
        {people.map((person) => (
          <Attachment
            placement="bottom-end"
            anchor="corner"
            shape="rect"
            hugCorners
          >
            <Avatar
              shape="square"
              size={size}
              name={person.name}
              src={person.src}
            />
            <span slot="content" class={dotClass(size, person.feedback)} />
          </Attachment>
        ))}
      </div>
    ))
  }
</div>
props · 26 shown · 26 total
active
boolean"current""checked""current-within"
default false

Mark this as the chosen one in a set (a current nav item, a checked option, a selected card). Where `interactive` says it *can* be clicked, `active` says it *is* the current choice. Use `activeEmphasis` to control how loud the state is. Combined with `interactive`, hover and press restart their stepping from the active baseline rather than falling back to the unchosen ramp. The three values differ only in what drives the state: `true` by the prop (via `data-panel-active`), `"checked"` by a wrapped native input (`:has(input:checked)` — zero JS, nothing to re-render), and `"current"` by an existing `aria-current`.

activeEmphasis
"primary""secondary""tertiary"
default the panel's `emphasis`

How loud the `active` state is. Defaults to the panel's own `emphasis`, so the chosen state lands on the same ramp as the resting look — this prop is the override for when the active item should be louder (or quieter) than the panel itself. `"primary"` fills with the feedback accent ramp and flips text to the contrast scale (~150 RGB units of separation — the option that reads at a glance in a dense list). `"secondary"` tints the feedback surface ramp and `"tertiary"` the neutral ramp; both promote text to `bold` and lift the border to the default line, but move the surface only ~7–18 units.

alt
string

Accessible alt text for the image. Defaults to `name`.

background
"surface.peak""surface.hill""surface.base""surface.valley""surface.ground""transparent"

Surface background from the Panel scale (`surface.peak` → `surface.ground`, or `transparent`).

border
"none""bold""default""muted""accent"

Border style: `none`, `default`, `bold`, or `muted`.

borderActive
"none""bold""inherit""default""muted""accent"

No description yet.

borderInteract
"none""bold""default""muted""accent"

Border style applied on hover / focus interaction.

element binding svelte
HTMLSpanElementnull

No description yet.

emphasis
"primary""muted""secondary""ghost"
default "secondary"

Emphasis level that tunes the avatar surface and fallback styling.

fallback slot svelte
Snippet

No description yet.

feedback
"primary""neutral""success""warning""danger""inherit"

Semantic color tone: `neutral`, `primary`, `success`, `warning`, `danger`, or `inherit`.

interactive
boolean

Enable interactive (hover / focus / press) affordances and states.

name
string

User name; initials are derived from the first and last word.

padding
"sm""md""lg""xl""2xl""none""xs""3xl""4xl""5xl""6xl""7xl""8xl""4xs""3xs""2xs""9xl""10xl""11xl"

Inner padding from the spacing scale.

passThrough
AvatarPassThrough<RootElementAttributes>

Per-slot style and HTML attribute override bag.

radius
"sm""md""lg""xl""2xl""none""xs""3xl""4xl""5xl""6xl""full""2xs""inner""inherit"

Corner radius override from the spacing scale (or `full` for fully rounded).

radiusBottom
"sm""md""lg""xl""2xl""none""xs""3xl""4xl""5xl""6xl""full""2xs""inner""inherit"

Corner radius for the bottom-left and bottom-right corners.

radiusLeft
"sm""md""lg""xl""2xl""none""xs""3xl""4xl""5xl""6xl""full""2xs""inner""inherit"

Corner radius for the top-left and bottom-left corners.

radiusRight
"sm""md""lg""xl""2xl""none""xs""3xl""4xl""5xl""6xl""full""2xs""inner""inherit"

Corner radius for the top-right and bottom-right corners.

radiusTop
"sm""md""lg""xl""2xl""none""xs""3xl""4xl""5xl""6xl""full""2xs""inner""inherit"

Corner radius for the top-left and top-right corners.

reserveBorderSpace
boolean

Keep (or drop) the always-1px transparent border the panel reserves so a border appearing or changing never shifts layout. Computed automatically — it's reserved when a resting `border` is visible, or when `interactive` / `borderInteract` / `active` can change the border at runtime. Set `false` only on a panel that must not occupy that 1px (e.g. a borderless housing frame that would otherwise add 2px around a set).

shadow
"sm""md""lg""xl""none""xs"

Elevation shadow applied to the surface.

shape
"square""circle"
default "circle"

Avatar shape: a circle or a square with a rounded corner.

size
"sm""md""lg""xl""xs"
default "md"

Avatar size; matches the button scale so an avatar and button of the same size align.

src
string

Image URL. Omit to render the fallback.

translucent
boolean

Apply a frosted-glass effect with backdrop blur over a surface background.

Plus all standard <span> HTML attributes.

Type

  • Components
  • Blocks