From cb842868f5cbc3216239cbd5ec3dea79922fbdd4 Mon Sep 17 00:00:00 2001 From: Naveed Sajjad <88137903+naveedsajjdawan@users.noreply.github.com> Date: Wed, 21 Oct 2026 13:43:21 +0500 Subject: [PATCH 1/5] feat(Avatar): support text placeholders --- packages/react/src/Avatar/Avatar.tsx | 66 ++++++++++++++++++---------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/packages/react/src/Avatar/Avatar.tsx b/packages/react/src/Avatar/Avatar.tsx index b9e501a8412..a3324234d10 100644 --- a/packages/react/src/Avatar/Avatar.tsx +++ b/packages/react/src/Avatar/Avatar.tsx @@ -11,16 +11,18 @@ export type AvatarProps = { size?: number | ResponsiveValue /** Sets the shape of the avatar to a square if true. If false, the avatar will be circular. */ square?: boolean - /** URL of the avatar image. */ - src: string + /** URL of the avatar image. When provided, the image takes precedence over the placeholder. */ + src?: string + /** Text to display when no image URL is provided, such as a user's initials. */ + placeholder?: string /** Provide alt text when the Avatar is used without the user's name next to it. */ alt?: string /** Additional class name. */ className?: string -} & React.ComponentPropsWithoutRef<'img'> +} & Omit, 'src'> -const Avatar = React.forwardRef(function Avatar( - {alt = '', size = DEFAULT_AVATAR_SIZE, square = false, className, style, ...rest}, +const Avatar = React.forwardRef(function Avatar( + {alt = '', size = DEFAULT_AVATAR_SIZE, square = false, placeholder, src, className, style, ...rest}, ref, ) { const isResponsive = isResponsiveValue(size) @@ -34,26 +36,42 @@ const Avatar = React.forwardRef(function Avatar( cssSizeVars['--avatarSize-regular'] = `${size}px` } + const sharedProps = { + 'data-component': 'Avatar', + className: clsx(className, classes.Avatar), + ref, + 'data-responsive': isResponsive ? '' : undefined, + 'data-square': square ? '' : undefined, + style: style + ? { + ...cssSizeVars, + ...style, + } + : (cssSizeVars as React.CSSProperties), + ...rest, + } + + if (src) { + return ( + {alt} + ) + } + return ( - {alt} + + {placeholder} + ) }) From bcb4bcab522971375c43b8ecd1fa9e483a683e56 Mon Sep 17 00:00:00 2001 From: Naveed Sajjad <88137903+naveedsajjdawan@users.noreply.github.com> Date: Wed, 21 Oct 2026 13:43:27 +0500 Subject: [PATCH 2/5] style(Avatar): center and scale text placeholders --- packages/react/src/Avatar/Avatar.module.css | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/react/src/Avatar/Avatar.module.css b/packages/react/src/Avatar/Avatar.module.css index fc67531b9a7..6641d530df4 100644 --- a/packages/react/src/Avatar/Avatar.module.css +++ b/packages/react/src/Avatar/Avatar.module.css @@ -10,6 +10,17 @@ /* stylelint-disable-next-line primer/box-shadow */ box-shadow: 0 0 0 1px var(--avatar-borderColor); + &:where([data-placeholder]) { + display: inline-flex; + align-items: center; + justify-content: center; + background: var(--bgColor-muted); + color: var(--fgColor-muted); + font-size: calc(var(--avatarSize-regular) * 0.4); + font-weight: var(--base-text-weight-semibold); + white-space: nowrap; + } + &:where([data-square]) { /* stylelint-disable-next-line primer/borders */ border-radius: clamp(4px, calc(var(--avatarSize-regular) - 24px), var(--borderRadius-medium)); From 6659029907389dbb1b172a1c421dd0d2d286fc21 Mon Sep 17 00:00:00 2001 From: Naveed Sajjad <88137903+naveedsajjdawan@users.noreply.github.com> Date: Wed, 21 Oct 2026 13:43:34 +0500 Subject: [PATCH 3/5] test(Avatar): cover text placeholder rendering --- packages/react/src/Avatar/Avatar.test.tsx | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/react/src/Avatar/Avatar.test.tsx b/packages/react/src/Avatar/Avatar.test.tsx index ba816856822..314934a2190 100644 --- a/packages/react/src/Avatar/Avatar.test.tsx +++ b/packages/react/src/Avatar/Avatar.test.tsx @@ -36,6 +36,31 @@ describe('Avatar', () => { expect(avatar).toHaveAttribute('src', 'primer.png') }) + it('renders a text placeholder when src is omitted', () => { + render() + const avatar = screen.getByTestId('avatar') + expect(avatar).toHaveTextContent('NS') + expect(avatar).toHaveAttribute('role', 'img') + expect(avatar).toHaveAttribute('aria-label', 'NS') + expect(avatar).toHaveAttribute('data-placeholder') + expect(avatar).toHaveAttribute('data-component', 'Avatar') + expect(avatar).not.toHaveAttribute('src') + }) + + it('prefers the image when both src and placeholder are provided', () => { + render() + const avatar = screen.getByTestId('avatar') + expect(avatar.tagName).toBe('IMG') + expect(avatar).toHaveAttribute('src', 'primer.png') + expect(avatar).toHaveAttribute('alt', 'Naveed') + expect(avatar).not.toHaveAttribute('data-placeholder') + }) + + it('uses alt text as the accessible name for a placeholder when provided', () => { + render() + expect(screen.getByTestId('avatar')).toHaveAttribute('aria-label', 'Naveed Sajjad') + }) + it('should support the `style` prop without overriding internal styles', () => { render( Date: Wed, 21 Oct 2026 13:43:41 +0500 Subject: [PATCH 4/5] fix(Avatar): preserve branch-specific ref types --- packages/react/src/Avatar/Avatar.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react/src/Avatar/Avatar.tsx b/packages/react/src/Avatar/Avatar.tsx index a3324234d10..d98dfb1bca3 100644 --- a/packages/react/src/Avatar/Avatar.tsx +++ b/packages/react/src/Avatar/Avatar.tsx @@ -39,7 +39,6 @@ const Avatar = React.forwardRef(function Avatar( const sharedProps = { 'data-component': 'Avatar', className: clsx(className, classes.Avatar), - ref, 'data-responsive': isResponsive ? '' : undefined, 'data-square': square ? '' : undefined, style: style @@ -55,6 +54,7 @@ const Avatar = React.forwardRef(function Avatar( return ( } alt={alt} width={isResponsive ? undefined : size} height={isResponsive ? undefined : size} @@ -66,6 +66,7 @@ const Avatar = React.forwardRef(function Avatar( return ( } role="img" aria-label={alt || placeholder || undefined} data-placeholder="" From b3f34ef5710c64be3376df538f637e9cb7c680e7 Mon Sep 17 00:00:00 2001 From: Naveed Sajjad <88137903+naveedsajjdawan@users.noreply.github.com> Date: Mon, 2 Nov 2026 14:34:05 +0500 Subject: [PATCH 5/5] chore: add Avatar placeholder changeset --- .changeset/small-avatars-smile.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/small-avatars-smile.md diff --git a/.changeset/small-avatars-smile.md b/.changeset/small-avatars-smile.md new file mode 100644 index 00000000000..24e0458bdec --- /dev/null +++ b/.changeset/small-avatars-smile.md @@ -0,0 +1,5 @@ +--- +'@primer/react': minor +--- + +Add text placeholder support to Avatar when no image source is provided.