# Avatar

A circular identity mark for a person, account, or brand, with resilient image fallbacks.

Version: 0.2.0-preview.1 (preview)
Content digest: 70ac710f4d22dce040e7ac593379ba8aca9dcb59a0b9c0f839819cae0e2bb721
Contract kind: source-derived
Source: packages/design-system/src/components/primitives/Avatar/Avatar.tsx

## Guidance

{
  "summary": "A circular identity mark for a person, account, or brand, with resilient image fallbacks.",
  "guidance": [
    {
      "heading": "Overview",
      "body": "A single identity mark for one person, account, or brand, rendered as an icon, initials,\na brand logo, or a photo. For several identities together use `AvatarGroup`."
    },
    {
      "heading": "Use when",
      "body": "Pick `type` for what the mark represents. `brand`/`image` fall back to `initial` when the\nsource fails or is rejected as too small or too wide (a wordmark).\n\n`size` maps to the token scale: `sm` 24, `md` 40, `lg` 56, `xl` 80. Reach for `IconTile`\ninstead when the circle is a category affordance whose color carries meaning."
    },
    {
      "heading": "Loading",
      "body": "Brand and image sources shimmer, then fade in once resolved."
    }
  ],
  "usage": [
    {
      "variant": "brand",
      "use": "A contained, padded, aspect-preserving company or service logo. The common case, resolved from brand assets.",
      "avoid": "A person — use initials or image.",
      "axis": "Type"
    },
    {
      "variant": "initials",
      "use": "A monogram, used as the fallback when no logo or photo is available. `color` sets the circle background.",
      "avoid": "When a real logo or photo exists — show it.",
      "axis": "Type"
    },
    {
      "variant": "icon",
      "use": "A glyph identity mark, for accounts represented by an icon rather than a logo.",
      "avoid": "A category tile whose tone carries meaning — use `IconTile`.",
      "axis": "Type"
    },
    {
      "variant": "image",
      "use": "An edge-to-edge cover photo for a person. (No current app consumers.)",
      "avoid": "A logo — use brand, which pads and preserves aspect ratio.",
      "axis": "Type"
    }
  ],
  "examples": [],
  "source": "packages/design-system/src/components/primitives/Avatar/Avatar.mdx",
  "status": "local-preview",
  "note": "Guidance is read from local documentation, originally imported from the frozen source. Platform assertions in guidance are not parity verification."
}

## Contract and public API

```json
{
  "props": [
    {
      "name": "type",
      "type": "AvatarType",
      "declared_type": "AvatarType",
      "required": true,
      "description": "Which avatar is rendered. `brand` insets the mark and contains it inside the circle; `image`\ncrops to fill. `icon` and `initials` take `icon`/`initial` instead of `src`.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 21
      },
      "values": [
        "brand",
        "icon",
        "image",
        "initials"
      ],
      "values_complete": true
    },
    {
      "name": "size",
      "type": "AvatarSize",
      "declared_type": "AvatarSize",
      "required": true,
      "description": "Named size, mapped to the sizing token scale.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 23
      },
      "values": [
        "lg",
        "md",
        "sm",
        "xl"
      ],
      "values_complete": true
    },
    {
      "name": "icon",
      "type": "React.ReactNode",
      "declared_type": "React.ReactNode",
      "required": false,
      "description": "Rendered as-is for `type='icon'`. The avatar sets no size on it, so size the node yourself.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 25
      }
    },
    {
      "name": "initial",
      "type": "string | undefined",
      "declared_type": "string",
      "required": false,
      "description": "Initials text for `type='initials'`, and what a `brand`/`image` avatar renders once no source\ncan be shown — omit it and those render nothing. Uppercased by CSS.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 30
      }
    },
    {
      "name": "color",
      "type": "string | undefined",
      "declared_type": "string",
      "required": false,
      "description": "Background color for initials avatars.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 32
      }
    },
    {
      "name": "initialTextColor",
      "type": "string | undefined",
      "declared_type": "string",
      "required": false,
      "description": "Initials text color. Defaults to `--cds-text-on-bold`.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 34
      }
    },
    {
      "name": "brandFrame",
      "type": "AvatarBrandFrame | undefined",
      "declared_type": "AvatarBrandFrame",
      "required": false,
      "description": "Edge treatment for `brand` avatars. Use `none` only for a mark you ship and have looked at;\na runtime-resolved mark has an unknowable silhouette, so it keeps the ring.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 39
      },
      "values": [
        "none",
        "ring"
      ],
      "values_complete": true,
      "default": "'ring'",
      "default_kind": "source-expression"
    },
    {
      "name": "src",
      "type": "string | undefined",
      "declared_type": "string",
      "required": false,
      "description": "Image source for `brand` and `image` avatars.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 41
      }
    },
    {
      "name": "fallbackSrc",
      "type": "string | undefined",
      "declared_type": "string",
      "required": false,
      "description": "Secondary image tried before initials when the primary source is unavailable.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 43
      }
    },
    {
      "name": "minImageNaturalWidth",
      "type": "number | undefined",
      "declared_type": "number",
      "required": false,
      "description": "Minimum loaded image dimension before falling back to initials.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 45
      }
    },
    {
      "name": "fallbackMinImageNaturalWidth",
      "type": "number | undefined",
      "declared_type": "number",
      "required": false,
      "description": "Minimum loaded fallback image dimension before falling back to initials.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 47
      }
    },
    {
      "name": "maxImageAspectRatio",
      "type": "number | undefined",
      "declared_type": "number",
      "required": false,
      "description": "Maximum loaded image aspect ratio (longer / shorter side) before falling back; rejects wide wordmarks.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 49
      }
    },
    {
      "name": "fallbackMaxImageAspectRatio",
      "type": "number | undefined",
      "declared_type": "number",
      "required": false,
      "description": "Maximum loaded fallback image aspect ratio before falling back to initials.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 51
      }
    },
    {
      "name": "alt",
      "type": "string | undefined",
      "declared_type": "string",
      "required": false,
      "description": "Accessible image description.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 53
      }
    },
    {
      "name": "ariaLabel",
      "type": "string | undefined",
      "declared_type": "string",
      "required": false,
      "description": "Accessible label for non-image avatars.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 55
      }
    },
    {
      "name": "className",
      "type": "string | undefined",
      "declared_type": "string",
      "required": false,
      "description": "Class hook for composite styling. Applied to the avatar root for every type.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 57
      }
    },
    {
      "name": "imageClassName",
      "type": "string | undefined",
      "declared_type": "string",
      "required": false,
      "description": "Class applied only to the `<img>` of `brand`/`image` avatars (never the initials fallback).",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 59
      }
    },
    {
      "name": "style",
      "type": "React.CSSProperties | undefined",
      "declared_type": "React.CSSProperties",
      "required": false,
      "description": "",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 60
      }
    },
    {
      "name": "testId",
      "type": "string | undefined",
      "declared_type": "string",
      "required": false,
      "description": "Optional test selector. Applied to the avatar root, including a standing-in initials element.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 62
      }
    }
  ],
  "prop_shapes": {},
  "api": {
    "method": "typescript-type-checker",
    "typescript_version": "5.9.3",
    "public_export": "Avatar",
    "public_path": "Avatar",
    "import_path": "@cloakedinc/design-system",
    "root_kind": "component",
    "entrypoints": [
      {
        "name": "Avatar",
        "signature": 1,
        "visibility": "public",
        "source": {
          "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
          "line": 69
        },
        "props_type": "AvatarProps",
        "props": [
          {
            "name": "type",
            "type": "AvatarType",
            "declared_type": "AvatarType",
            "required": true,
            "description": "Which avatar is rendered. `brand` insets the mark and contains it inside the circle; `image`\ncrops to fill. `icon` and `initials` take `icon`/`initial` instead of `src`.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 21
            },
            "values": [
              "brand",
              "icon",
              "image",
              "initials"
            ],
            "values_complete": true
          },
          {
            "name": "size",
            "type": "AvatarSize",
            "declared_type": "AvatarSize",
            "required": true,
            "description": "Named size, mapped to the sizing token scale.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 23
            },
            "values": [
              "lg",
              "md",
              "sm",
              "xl"
            ],
            "values_complete": true
          },
          {
            "name": "icon",
            "type": "React.ReactNode",
            "declared_type": "React.ReactNode",
            "required": false,
            "description": "Rendered as-is for `type='icon'`. The avatar sets no size on it, so size the node yourself.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 25
            }
          },
          {
            "name": "initial",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "Initials text for `type='initials'`, and what a `brand`/`image` avatar renders once no source\ncan be shown — omit it and those render nothing. Uppercased by CSS.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 30
            }
          },
          {
            "name": "color",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "Background color for initials avatars.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 32
            }
          },
          {
            "name": "initialTextColor",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "Initials text color. Defaults to `--cds-text-on-bold`.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 34
            }
          },
          {
            "name": "brandFrame",
            "type": "AvatarBrandFrame | undefined",
            "declared_type": "AvatarBrandFrame",
            "required": false,
            "description": "Edge treatment for `brand` avatars. Use `none` only for a mark you ship and have looked at;\na runtime-resolved mark has an unknowable silhouette, so it keeps the ring.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 39
            },
            "values": [
              "none",
              "ring"
            ],
            "values_complete": true,
            "default": "'ring'",
            "default_kind": "source-expression"
          },
          {
            "name": "src",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "Image source for `brand` and `image` avatars.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 41
            }
          },
          {
            "name": "fallbackSrc",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "Secondary image tried before initials when the primary source is unavailable.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 43
            }
          },
          {
            "name": "minImageNaturalWidth",
            "type": "number | undefined",
            "declared_type": "number",
            "required": false,
            "description": "Minimum loaded image dimension before falling back to initials.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 45
            }
          },
          {
            "name": "fallbackMinImageNaturalWidth",
            "type": "number | undefined",
            "declared_type": "number",
            "required": false,
            "description": "Minimum loaded fallback image dimension before falling back to initials.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 47
            }
          },
          {
            "name": "maxImageAspectRatio",
            "type": "number | undefined",
            "declared_type": "number",
            "required": false,
            "description": "Maximum loaded image aspect ratio (longer / shorter side) before falling back; rejects wide wordmarks.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 49
            }
          },
          {
            "name": "fallbackMaxImageAspectRatio",
            "type": "number | undefined",
            "declared_type": "number",
            "required": false,
            "description": "Maximum loaded fallback image aspect ratio before falling back to initials.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 51
            }
          },
          {
            "name": "alt",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "Accessible image description.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 53
            }
          },
          {
            "name": "ariaLabel",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "Accessible label for non-image avatars.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 55
            }
          },
          {
            "name": "className",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "Class hook for composite styling. Applied to the avatar root for every type.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 57
            }
          },
          {
            "name": "imageClassName",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "Class applied only to the `<img>` of `brand`/`image` avatars (never the initials fallback).",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 59
            }
          },
          {
            "name": "style",
            "type": "React.CSSProperties | undefined",
            "declared_type": "React.CSSProperties",
            "required": false,
            "description": "",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 60
            }
          },
          {
            "name": "testId",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "Optional test selector. Applied to the avatar root, including a standing-in initials element.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
              "line": 62
            }
          }
        ],
        "branches": [],
        "constraints": [],
        "extraction_status": "extracted"
      }
    ],
    "type_sources": [
      {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 6,
        "name": "AvatarType",
        "code": "export type AvatarType = 'icon' | 'initials' | 'brand' | 'image';"
      },
      {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 8,
        "name": "AvatarSize",
        "code": "export type AvatarSize = 'sm' | 'md' | 'lg' | 'xl';"
      },
      {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 10,
        "name": "AvatarBrandFrame",
        "code": "export type AvatarBrandFrame = 'ring' | 'none';"
      },
      {
        "path": "packages/design-system/src/components/primitives/Avatar/Avatar.tsx",
        "line": 16,
        "name": "AvatarProps",
        "code": "export interface AvatarProps {\n    /**\n     * Which avatar is rendered. `brand` insets the mark and contains it inside the circle; `image`\n     * crops to fill. `icon` and `initials` take `icon`/`initial` instead of `src`.\n     */\n    type: AvatarType;\n    /** Named size, mapped to the sizing token scale. */\n    size: AvatarSize;\n    /** Rendered as-is for `type='icon'`. The avatar sets no size on it, so size the node yourself. */\n    icon?: React.ReactNode;\n    /**\n     * Initials text for `type='initials'`, and what a `brand`/`image` avatar renders once no source\n     * can be shown — omit it and those render nothing. Uppercased by CSS.\n     */\n    initial?: string;\n    /** Background color for initials avatars. */\n    color?: string;\n    /** Initials text color. Defaults to `--cds-text-on-bold`. */\n    initialTextColor?: string;\n    /**\n     * Edge treatment for `brand` avatars. Use `none` only for a mark you ship and have looked at;\n     * a runtime-resolved mark has an unknowable silhouette, so it keeps the ring.\n     */\n    brandFrame?: AvatarBrandFrame;\n    /** Image source for `brand` and `image` avatars. */\n    src?: string;\n    /** Secondary image tried before initials when the primary source is unavailable. */\n    fallbackSrc?: string;\n    /** Minimum loaded image dimension before falling back to initials. */\n    minImageNaturalWidth?: number;\n    /** Minimum loaded fallback image dimension before falling back to initials. */\n    fallbackMinImageNaturalWidth?: number;\n    /** Maximum loaded image aspect ratio (longer / shorter side) before falling back; rejects wide wordmarks. */\n    maxImageAspectRatio?: number;\n    /** Maximum loaded fallback image aspect ratio before falling back to initials. */\n    fallbackMaxImageAspectRatio?: number;\n    /** Accessible image description. */\n    alt?: string;\n    /** Accessible label for non-image avatars. */\n    ariaLabel?: string;\n    /** Class hook for composite styling. Applied to the avatar root for every type. */\n    className?: string;\n    /** Class applied only to the `<img>` of `brand`/`image` avatars (never the initials fallback). */\n    imageClassName?: string;\n    style?: React.CSSProperties;\n    /** Optional test selector. Applied to the avatar root, including a standing-in initials element. */\n    testId?: string;\n}"
      }
    ],
    "defaults_policy": "Only explicit destructured parameter initializers are extracted. Other runtime defaults remain in implementation source."
  },
  "spec": {
    "$schema": "../../../specs/component-spec.schema.json",
    "identity": {
      "name": "Avatar",
      "status": "preview",
      "version": "1.2.0",
      "summary": "A circular identity mark for a person, account, or brand, with resilient image fallbacks."
    },
    "parts": [
      {
        "id": "container",
        "label": "Container",
        "description": "Owns the circular crop, background role, and selected size."
      },
      {
        "id": "identity",
        "label": "Identity content",
        "description": "Shows an icon, initials, contained brand mark, or edge-to-edge image."
      },
      {
        "id": "placeholder",
        "label": "Loading placeholder",
        "description": "Shimmers while a brand or image source is loading."
      }
    ],
    "allowedDesignChoices": {
      "variants": {
        "icon": {
          "label": "Icon"
        },
        "initials": {
          "label": "Initials"
        },
        "brand": {
          "label": "Brand"
        },
        "image": {
          "label": "Image"
        }
      },
      "sizes": {
        "sm": {
          "label": "Small"
        },
        "md": {
          "label": "Medium"
        },
        "lg": {
          "label": "Large"
        },
        "xl": {
          "label": "Extra large"
        }
      },
      "brandFrame": {
        "ring": {
          "label": "Ring",
          "description": "Default. Hairline edge for a mark whose silhouette is unknown at author time."
        },
        "none": {
          "label": "None",
          "description": "No edge, for a bundled mark whose own shape has been checked."
        }
      }
    },
    "variants": [
      {
        "id": "icon",
        "label": "Icon",
        "description": "Centers a glyph when an account is represented by an icon rather than a logo.",
        "platforms": [
          "ios",
          "android",
          "web"
        ]
      },
      {
        "id": "initials",
        "label": "Initials",
        "description": "Shows an uppercase monogram and serves as the final image fallback.",
        "platforms": [
          "ios",
          "android",
          "web"
        ]
      },
      {
        "id": "brand",
        "label": "Brand",
        "description": "Contains a brand mark without cropping its aspect ratio.",
        "platforms": [
          "ios",
          "android",
          "web"
        ]
      },
      {
        "id": "image",
        "label": "Image",
        "description": "Crops a person image edge to edge with cover behavior.",
        "platforms": [
          "ios",
          "android",
          "web"
        ]
      }
    ],
    "sizes": [
      {
        "id": "sm",
        "label": "Small",
        "description": "24-unit compact identity mark."
      },
      {
        "id": "md",
        "label": "Medium",
        "description": "40-unit default identity mark."
      },
      {
        "id": "lg",
        "label": "Large",
        "description": "56-unit prominent identity mark."
      },
      {
        "id": "xl",
        "label": "Extra large",
        "description": "80-unit hero identity mark."
      }
    ],
    "measurements": [
      {
        "id": "sm-diameter",
        "label": "Small diameter",
        "kind": "exact",
        "token": "--cds-size-24",
        "value": 24,
        "unit": "px",
        "appliesTo": [
          "container",
          "sm"
        ]
      },
      {
        "id": "md-diameter",
        "label": "Medium diameter",
        "kind": "exact",
        "token": "--cds-size-40",
        "value": 40,
        "unit": "px",
        "appliesTo": [
          "container",
          "md"
        ]
      },
      {
        "id": "lg-diameter",
        "label": "Large diameter",
        "kind": "exact",
        "token": "--cds-size-56",
        "value": 56,
        "unit": "px",
        "appliesTo": [
          "container",
          "lg"
        ]
      },
      {
        "id": "xl-diameter",
        "label": "Extra-large diameter",
        "kind": "exact",
        "token": "--cds-size-80",
        "value": 80,
        "unit": "px",
        "appliesTo": [
          "container",
          "xl"
        ]
      },
      {
        "id": "brand-mark-inset",
        "label": "Brand mark inset",
        "kind": "exact",
        "value": 12,
        "unit": "%",
        "appliesTo": [
          "identity",
          "brand"
        ],
        "notes": "Proportional padding so the brand mark stays inside the circular clip. Empirically fit to live favicon-service/brandfetch assets, which carry some internal transparent margin: the measured LinkedIn/YouTube/Reddit marks lose up to ~12.2% of opaque pixels to the crop at 0% inset, and 12% takes all three to zero. It is not a geometric guarantee — a true edge-to-edge square with hard corners needs ~14.65%, since its corners sit 0.537x the diameter from center against a 0.5x radius."
      },
      {
        "id": "brand-ring-width",
        "label": "Brand chip ring width",
        "kind": "exact",
        "token": "--cds-border-width-default",
        "value": 1,
        "unit": "px",
        "appliesTo": [
          "container",
          "brand"
        ],
        "notes": "Inset hairline ring on the brand chip, painted as `box-shadow: inset` rather than `border` so it doesn't perturb the box model the avatar's fixed size and the brand mark's 12% inset both depend on. Brand marks arrive as a circular badge, a wide rounded rect, and a square with no shared silhouette; the ring gives every shape one consistent edge. Present by default and suppressed by `brandFrame='none'`, which is reserved for a bundled mark whose silhouette the author has checked — a runtime-resolved mark keeps the ring because its shape cannot be known."
      }
    ],
    "layout": {
      "alignment": "Identity content is centered. Person/cover images fill the circular crop edge to edge; brand marks are inset and contained, so they never reach the crop.",
      "flow": "The avatar is an inline, non-shrinking circle.",
      "notes": [
        "Brand images use contain behavior with a 12% inset while person images use cover behavior edge to edge.",
        "The brand chip carries an inset hairline ring unless `brandFrame='none'`; the image chip never does.",
        "Initials scale with the selected diameter."
      ]
    },
    "typographyAndColor": {
      "typography": [
        {
          "label": "Initials foundation",
          "description": "Initials begin from the body-small ramp before scaling with the avatar diameter.",
          "token": "--cds-type-body-small-size"
        }
      ],
      "colors": [
        {
          "label": "Default initials container",
          "description": "Uses the theme-aware bold fill unless a consumer supplies an identity color.",
          "token": "--cds-bg-bold"
        },
        {
          "label": "Default initials text",
          "description": "Maintains contrast on the bold fill.",
          "token": "--cds-text-on-bold"
        },
        {
          "label": "Image surface",
          "description": "Backs brand and image content while it loads.",
          "token": "--cds-bg-surface"
        },
        {
          "label": "Brand chip ring",
          "description": "Hairline ring around the brand chip so mixed brand-mark silhouettes read as one consistent shape. Suppressed by `brandFrame='none'`.",
          "token": "--cds-divider"
        }
      ]
    },
    "states": [
      {
        "id": "ready",
        "label": "Ready",
        "description": "Shows the chosen icon, initials, or loaded image."
      },
      {
        "id": "loading",
        "label": "Loading",
        "description": "Shows a circular shimmer while an image source resolves."
      },
      {
        "id": "fallback-image",
        "label": "Fallback image",
        "description": "Tries the secondary source when the primary source fails validation or loading."
      },
      {
        "id": "fallback-initials",
        "label": "Fallback initials",
        "description": "Shows initials when no acceptable image source remains."
      }
    ],
    "behavior": [
      {
        "label": "Image validation",
        "description": "Reject images that fall below configured natural-width or aspect-ratio limits."
      },
      {
        "label": "Fallback order",
        "description": "Try the primary image, then fallback image, then initials when supplied."
      }
    ],
    "accessibility": [
      {
        "label": "Images",
        "description": "Use alt text for meaningful images and an empty alt value for decorative identity marks."
      },
      {
        "label": "Non-image marks",
        "description": "Give icon avatars an accessible label; initials fall back to the visible monogram."
      }
    ],
    "contentAdaptation": [
      {
        "label": "Initials",
        "description": "Keep monograms short enough to remain legible at the selected size."
      },
      {
        "label": "Brand assets",
        "description": "Prefer compact marks over wide wordmarks; aspect-ratio validation may reject a wide source."
      }
    ],
    "motionHaptics": {
      "motion": [
        {
          "label": "Image arrival",
          "description": "Fade a loaded bitmap over the placeholder; remove the fade for reduced motion. Reveal a bitmap that was already decoded at mount outright — no placeholder, no fade — so a list of cached avatars does not animate content that was never absent. An image whose source was decoded earlier in the session still shows its placeholder until its own bitmap arrives, then skips the fade."
        }
      ],
      "haptics": [
        {
          "label": "No feedback",
          "description": "Avatar is not interactive and does not request haptic feedback."
        }
      ]
    },
    "platformDifferences": []
  },
  "platforms": {
    "implemented": [
      "web"
    ],
    "native": "Imported design guidance only; no native implementation or parity verification in this preview.",
    "react": {
      "implementation": "local",
      "status": "preview",
      "parity": "web-reference"
    },
    "vue": {
      "implementation": "absent",
      "parity": "not-verified",
      "status": "reference-only"
    },
    "swift": {
      "implementation": "absent",
      "parity": "not-verified",
      "status": "reference-only"
    },
    "kotlin": {
      "implementation": "absent",
      "parity": "not-verified",
      "status": "reference-only"
    }
  },
  "tokens": [
    {
      "name": "--cds-size-24",
      "light": "24px",
      "dark": "24px"
    },
    {
      "name": "--cds-size-40",
      "light": "40px",
      "dark": "40px"
    },
    {
      "name": "--cds-size-56",
      "light": "56px",
      "dark": "56px"
    },
    {
      "name": "--cds-size-80",
      "light": "80px",
      "dark": "80px"
    },
    {
      "name": "--cds-border-width-default",
      "light": "1px",
      "dark": "1px"
    },
    {
      "name": "--cds-type-body-small-size",
      "light": "0.875rem",
      "dark": "0.875rem"
    },
    {
      "name": "--cds-bg-bold",
      "light": "#0b0b0a",
      "dark": "#ffffff"
    },
    {
      "name": "--cds-text-on-bold",
      "light": "#ffffff",
      "dark": "#141410"
    },
    {
      "name": "--cds-bg-surface",
      "light": "#ffffff",
      "dark": "#1b1b18"
    },
    {
      "name": "--cds-divider",
      "light": "#e0ddd6",
      "dark": "#ffffff1a"
    }
  ],
  "review": {
    "local_status": "not-reviewed",
    "source_approval": null,
    "imported_spec_status": "approved",
    "spec_origin": "imported-adapted",
    "local_spec_source_status": "approved",
    "imported_source_revision": "f96f416e1c3bfbfaffcd155a88b87b18b8bb25f1",
    "note": "Inherited specification status is provenance, not approval of the current local implementation or extracted contract."
  },
  "missing_facts": [
    {
      "code": "human-review-pending",
      "detail": "This local preview has no recorded human design or engineering sign-off."
    },
    {
      "code": "platform-parity-unverified",
      "detail": "Vue, Swift and Kotlin implementations are absent from this package; platform parity has not been verified."
    },
    {
      "code": "figma-mapping-unverified",
      "detail": "No current component-level Figma node and immutable revision have been verified for this contract."
    }
  ]
}
```

## Visual states

- [Playground](/storybook/iframe.html?id=cloaked-design-system-primitives-avatar--playground&viewMode=story) | export: Playground | source: packages/design-system/src/components/primitives/Avatar/Avatar.stories.tsx

- [Icon](/storybook/iframe.html?id=cloaked-design-system-primitives-avatar--icon&viewMode=story) | export: Icon | source: packages/design-system/src/components/primitives/Avatar/Avatar.stories.tsx

- [Initials](/storybook/iframe.html?id=cloaked-design-system-primitives-avatar--initials&viewMode=story) | export: Initials | source: packages/design-system/src/components/primitives/Avatar/Avatar.stories.tsx

- [Brand](/storybook/iframe.html?id=cloaked-design-system-primitives-avatar--brand&viewMode=story) | export: Brand | source: packages/design-system/src/components/primitives/Avatar/Avatar.stories.tsx

- [Image](/storybook/iframe.html?id=cloaked-design-system-primitives-avatar--image&viewMode=story) | export: Image | source: packages/design-system/src/components/primitives/Avatar/Avatar.stories.tsx

- [Sizes](/storybook/iframe.html?id=cloaked-design-system-primitives-avatar--sizes&viewMode=story) | export: Sizes | source: packages/design-system/src/components/primitives/Avatar/Avatar.stories.tsx

- [Styled And Labeled](/storybook/iframe.html?id=cloaked-design-system-primitives-avatar--styled-and-labeled&viewMode=story) | export: StyledAndLabeled | source: packages/design-system/src/components/primitives/Avatar/Avatar.stories.tsx

- [Brand Loading](/storybook/iframe.html?id=cloaked-design-system-primitives-avatar--brand-loading&viewMode=story) | export: BrandLoading | source: packages/design-system/src/components/primitives/Avatar/Avatar.stories.tsx

- [Brand Frame None](/storybook/iframe.html?id=cloaked-design-system-primitives-avatar--brand-frame-none&viewMode=story) | export: BrandFrameNone | source: packages/design-system/src/components/primitives/Avatar/Avatar.stories.tsx

- [Dark Theme](/storybook/iframe.html?id=cloaked-design-system-primitives-avatar--dark-theme&viewMode=story) | export: DarkTheme | source: packages/design-system/src/components/primitives/Avatar/Avatar.stories.tsx

Preview pending human sign-off. Source-derived APIs and successful checks do not certify accessibility, native parity or approval.
