diff --git a/cypress/component/Avatar.spec.tsx b/cypress/component/Avatar.spec.tsx
index 5e7ad071a7..8c9153dc6d 100644
--- a/cypress/component/Avatar.spec.tsx
+++ b/cypress/component/Avatar.spec.tsx
@@ -1,6 +1,22 @@
+import {DecorativeInitials} from '../../modules/react/avatar/stories/examples/DecorativeInitials';
import {Image} from '../../modules/react/avatar/stories/examples/Image';
describe('Avatar', () => {
+ context('Decorative initials', () => {
+ beforeEach(() => {
+ cy.mount();
+ });
+
+ it('should not have any axe errors', () => {
+ cy.checkA11y();
+ });
+
+ it('should keep the initials visible but hidden from assistive technology beside the name', () => {
+ cy.findByText('NS').should('be.visible').and('have.attr', 'aria-hidden', 'true');
+ cy.findByText('Nicholas Smith').should('be.visible').and('not.have.attr', 'aria-hidden');
+ });
+ });
+
context(`Image Example`, () => {
beforeEach(() => {
// Stub the remote image so this spec does not depend on picsum.photos (blocked/slow in CI).
diff --git a/modules/react/avatar/lib/Avatar.tsx b/modules/react/avatar/lib/Avatar.tsx
index 68fefe264a..13523f0fa8 100644
--- a/modules/react/avatar/lib/Avatar.tsx
+++ b/modules/react/avatar/lib/Avatar.tsx
@@ -19,7 +19,9 @@ export interface AvatarProps extends BaseAvatarProps, AvatarNameProps {
*/
objectFit?: Property.ObjectFit;
/**
- * If true, the Avatar won't forward the `name` prop to the `alt` attribute of the image. This is useful when the Avatar is purely decorative and is rendered next to a name or text.
+ * If true, hides the image and fallback initials from assistive technology. This is useful when
+ * the Avatar is purely decorative and is rendered next to a name or text.
+ * @default false
*/
isDecorative?: boolean;
}
@@ -147,6 +149,7 @@ export const Avatar = createComponent('div')({
)}
diff --git a/modules/react/avatar/spec/Avatar.spec.tsx b/modules/react/avatar/spec/Avatar.spec.tsx
index f167b8374c..0f849c7123 100644
--- a/modules/react/avatar/spec/Avatar.spec.tsx
+++ b/modules/react/avatar/spec/Avatar.spec.tsx
@@ -1,9 +1,46 @@
-import {render, waitFor} from '@testing-library/react';
+import {fireEvent, isInaccessible, render, waitFor} from '@testing-library/react';
import * as React from 'react';
import {Avatar} from '../lib/Avatar';
describe('Avatar', () => {
+ verifyComponent(Avatar, {props: {name: 'John Doe'}});
+
+ describe('decorative initials', () => {
+ it.each([undefined, false, true])(
+ 'should expose initials only when isDecorative is not true (%s)',
+ isDecorative => {
+ const screen = render();
+ const initials = screen.getByText('JD');
+
+ expect(initials).toBeVisible();
+ expect(isInaccessible(initials)).toBe(isDecorative === true);
+ }
+ );
+
+ it('should hide custom decorative initials from assistive technology', () => {
+ const screen = render();
+
+ expect(screen.getByText('J')).toBeVisible();
+ expect(isInaccessible(screen.getByText('J'))).toBe(true);
+ });
+
+ it('should keep decorative fallback initials hidden while loading and after an image error', () => {
+ const screen = render();
+ const initials = screen.getByText('JD');
+ const image = screen.container.querySelector('img')!;
+
+ expect(isInaccessible(initials)).toBe(true);
+ fireEvent.error(image);
+ expect(initials).toBeVisible();
+ expect(isInaccessible(initials)).toBe(true);
+
+ fireEvent.load(image);
+ expect(screen.queryByText('JD')).not.toBeInTheDocument();
+ expect(isInaccessible(image)).toBe(true);
+ });
+ });
+
it('shoudld show the initials JD given the name is John Doe', () => {
const screen = render();
diff --git a/modules/react/avatar/stories/Avatar.mdx b/modules/react/avatar/stories/Avatar.mdx
index 697ad7a248..905b13a2d7 100644
--- a/modules/react/avatar/stories/Avatar.mdx
+++ b/modules/react/avatar/stories/Avatar.mdx
@@ -1,16 +1,13 @@
import {Meta} from '@storybook/blocks';
-import {
- ExampleCodeBlock,
- Specifications,
- SymbolDoc,
-} from '@workday/canvas-kit-docs';
+import {ExampleCodeBlock, Specifications, SymbolDoc} from '@workday/canvas-kit-docs';
import {Avatar} from '@workday/canvas-kit-react/avatar';
import {Basic} from './examples/Basic';
import {Custom} from './examples/Custom';
import {Decorative} from './examples/Decorative';
+import {DecorativeInitials} from './examples/DecorativeInitials';
import {Image} from './examples/Image';
import {Size} from './examples/Size';
import {Variant} from './examples/Variant';
@@ -82,11 +79,15 @@ For complete control over styling and behavior, use the `BaseAvatar` component:
### Accessibility
-If the Avatar is purely decorative, you can set the `isDecorative` prop to `true` to prevent the
-`name` prop from being forwarded to the `alt` attribute of the image.
+If the Avatar is rendered next to the user's name, set `isDecorative` to `true` to hide the image
+and fallback initials from assistive technology. This avoids announcing the same name twice.
+The same applies when no image is provided:
+
+
+
## Component API
diff --git a/modules/react/avatar/stories/Avatar.stories.ts b/modules/react/avatar/stories/Avatar.stories.ts
index ca7a545717..0a0ba33811 100644
--- a/modules/react/avatar/stories/Avatar.stories.ts
+++ b/modules/react/avatar/stories/Avatar.stories.ts
@@ -6,6 +6,7 @@ import mdxDoc from './Avatar.mdx';
import {Basic as BasicExample} from './examples/Basic';
import {Custom as CustomExample} from './examples/Custom';
import {Decorative as DecorativeExample} from './examples/Decorative';
+import {DecorativeInitials as DecorativeInitialsExample} from './examples/DecorativeInitials';
import {Image as ImageExample} from './examples/Image';
import {Size as SizeExample} from './examples/Size';
import {Variant as VariantExample} from './examples/Variant';
@@ -46,3 +47,7 @@ export const Custom: Story = {
export const Decorative: Story = {
render: DecorativeExample,
};
+
+export const DecorativeInitials: Story = {
+ render: DecorativeInitialsExample,
+};
diff --git a/modules/react/avatar/stories/examples/DecorativeInitials.tsx b/modules/react/avatar/stories/examples/DecorativeInitials.tsx
new file mode 100644
index 0000000000..7b65a9e805
--- /dev/null
+++ b/modules/react/avatar/stories/examples/DecorativeInitials.tsx
@@ -0,0 +1,18 @@
+import {Avatar} from '@workday/canvas-kit-react/avatar';
+import {Text} from '@workday/canvas-kit-react/text';
+import {createStyles} from '@workday/canvas-kit-styling';
+import {system} from '@workday/canvas-tokens-web';
+
+const containerStyles = createStyles({
+ display: 'inline-flex',
+ gap: system.gap.sm,
+ alignItems: 'center',
+});
+export const DecorativeInitials = () => {
+ return (
+
+ );
+};