diff --git a/docs/data/material/components/avatars/AvatarA11yImage.js b/docs/data/material/components/avatars/AvatarA11yImage.js
new file mode 100644
index 00000000000000..82b30a04f9d2e4
--- /dev/null
+++ b/docs/data/material/components/avatars/AvatarA11yImage.js
@@ -0,0 +1,17 @@
+import Avatar from '@mui/material/Avatar';
+import Stack from '@mui/material/Stack';
+
+// A 1x1 data-URI image so a real `
` renders in the regression harness,
+// which blocks network image requests (see test/regressions/index.test.js).
+// This exercises the working-image path (`alt` forwarded to a native `
`)
+// for the axe `image-alt` rule (WCAG 1.1.1).
+const dataUri =
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVR4nGNgYAAAAAMAASsJTYQAAAAASUVORK5CYII=';
+
+export default function AvatarA11yImage() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/material/components/avatars/AvatarA11yImage.tsx b/docs/data/material/components/avatars/AvatarA11yImage.tsx
new file mode 100644
index 00000000000000..82b30a04f9d2e4
--- /dev/null
+++ b/docs/data/material/components/avatars/AvatarA11yImage.tsx
@@ -0,0 +1,17 @@
+import Avatar from '@mui/material/Avatar';
+import Stack from '@mui/material/Stack';
+
+// A 1x1 data-URI image so a real `
` renders in the regression harness,
+// which blocks network image requests (see test/regressions/index.test.js).
+// This exercises the working-image path (`alt` forwarded to a native `
`)
+// for the axe `image-alt` rule (WCAG 1.1.1).
+const dataUri =
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVR4nGNgYAAAAAMAASsJTYQAAAAASUVORK5CYII=';
+
+export default function AvatarA11yImage() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/material/components/avatars/AvatarA11yImage.tsx.preview b/docs/data/material/components/avatars/AvatarA11yImage.tsx.preview
new file mode 100644
index 00000000000000..c536fdb8d1092c
--- /dev/null
+++ b/docs/data/material/components/avatars/AvatarA11yImage.tsx.preview
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/data/material/components/avatars/avatars.a11y.json b/docs/data/material/components/avatars/avatars.a11y.json
new file mode 100644
index 00000000000000..709f945561dfbb
--- /dev/null
+++ b/docs/data/material/components/avatars/avatars.a11y.json
@@ -0,0 +1,150 @@
+{
+ "AvatarA11yImage": {
+ "rules": {
+ "aria-allowed-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-conditional-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-prohibited-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-valid-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-valid-attr-value": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "image-alt": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "nested-interactive": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ }
+ }
+ },
+ "BackgroundLetterAvatars": {
+ "rules": {
+ "aria-allowed-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-conditional-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-prohibited-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-valid-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-valid-attr-value": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "color-contrast": {
+ "status": "fail",
+ "tags": ["wcag2aa"]
+ }
+ }
+ },
+ "IconAvatars": {
+ "rules": {
+ "aria-allowed-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-conditional-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-hidden-focus": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-prohibited-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-valid-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-valid-attr-value": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ }
+ }
+ },
+ "LetterAvatars": {
+ "rules": {
+ "aria-allowed-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-conditional-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-prohibited-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-valid-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-valid-attr-value": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "color-contrast": {
+ "status": "incomplete",
+ "tags": ["wcag2aa"]
+ }
+ }
+ },
+ "VariantAvatars": {
+ "rules": {
+ "aria-allowed-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-conditional-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-hidden-focus": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-prohibited-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-valid-attr": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "aria-valid-attr-value": {
+ "status": "pass",
+ "tags": ["wcag2a"]
+ },
+ "color-contrast": {
+ "status": "incomplete",
+ "tags": ["wcag2aa"]
+ }
+ }
+ }
+}
diff --git a/packages/mui-material/src/Avatar/Avatar.test.js b/packages/mui-material/src/Avatar/Avatar.test.js
index c48d43d7fe50c9..b7efe308a22634 100644
--- a/packages/mui-material/src/Avatar/Avatar.test.js
+++ b/packages/mui-material/src/Avatar/Avatar.test.js
@@ -278,4 +278,30 @@ describe('', () => {
),
).not.to.throw();
});
+
+ describe('WCAG 2.2 conformance', () => {
+ describe('1.3.1 Info and Relationships', () => {
+ it('renders a generic root with no ARIA role', () => {
+ const { container } = render();
+ expect(container.firstChild).to.have.tagName('div');
+ expect(container.firstChild).not.to.have.attribute('role');
+ });
+
+ it('hides the Person fallback from assistive technology', () => {
+ const { container } = render();
+ const fallback = container.querySelector('svg');
+ expect(fallback).to.have.attribute('data-testid', 'PersonIcon');
+ expect(fallback).to.have.attribute('aria-hidden', 'true');
+ });
+
+ it('hides a decorative SvgIcon child from assistive technology', () => {
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.querySelector('svg')).to.have.attribute('aria-hidden', 'true');
+ });
+ });
+ });
});
diff --git a/packages/mui-material/src/Avatar/accessibility.md b/packages/mui-material/src/Avatar/accessibility.md
new file mode 100644
index 00000000000000..fa4c2710044d67
--- /dev/null
+++ b/packages/mui-material/src/Avatar/accessibility.md
@@ -0,0 +1,208 @@
+# Avatar accessibility conformance
+
+Rated against WCAG 2.2 Level A and AA. See the [reports legend](../accessibility.md).
+
+| Result | Count |
+| :-------------------- | :---- |
+| ✅ Supports | 9 |
+| ⚠️ Partially Supports | 2 |
+| ❌ Does Not Support | 0 |
+| ➖ Not Applicable | 44 |
+| 🚩 Flagged | 5/11 |
+
+## Known gaps
+
+- ⚠️ **1.4.3 Contrast (Minimum).** Default letter and fallback avatars render white text on `grey[400]` (about 1.9:1), and the `deepOrange[500]` example is 3.16:1, both below 4.5:1; arbitrary `stringToColor` backgrounds are unguaranteed.
+- ⚠️ **1.4.11 Non-text Contrast.** Meaningful icon-child avatars use the default white-on-`grey[400]` (1.9:1) or low-contrast author backgrounds (for example `green[500]` 2.78:1), below the 3:1 minimum for graphical objects.
+
+## Success criteria
+
+### 🔍 Manual
+
+#### 1.3.2 Meaningful Sequence · A
+
+`🚩` · `✅ Supports` · `○ Author`
+
+- The component renders a single child (the `
`, the initials text, or one icon) in DOM order, so within one avatar there is no sequence whose order conveys meaning.
+- Order matters only across several avatars (a row, a list, or an `AvatarGroup`), which the surrounding layout controls. Confirm that the reading order of a group of avatars matches their visual order.
+
+**Manual testing steps**
+
+1. In a UI with a row of avatars (an `AvatarGroup`, or avatars in a list), inspect the DOM or accessibility tree order.
+2. Compare it to the visual left-to-right order.
+
+**Pass:** reading order matches visual order. A `Stack` or flex layout that visually reorders avatars without changing the DOM would fail.
+
+#### 1.3.3 Sensory Characteristics · A
+
+`🚩` · `✅ Supports` · `○ Author`
+
+- An avatar is identified by its image `alt`, its initials, or an author-supplied label, not by its shape (`variant`), color, size, or position, so it can be referenced without relying on a sensory characteristic.
+- Instructions in the surrounding content must not point to an avatar by shape or position alone (for example, "click the round picture on the left").
+
+**Manual testing steps**
+
+1. Find any product copy that tells users to act on an avatar.
+2. Check that it names the avatar by a text cue (the person's name or label), not only by shape, color, size, or position.
+
+**Pass:** no instruction relies on "the round avatar" or "the one on the right" without a text identifier.
+
+#### 1.4.4 Resize Text · AA
+
+`🚩` · `✅ Supports` · `● Component`
+
+- The container is a fixed 40px square with text in rem (`pxToRem(20)`) and `overflow: hidden`. Under full-page browser zoom (the default scaling mechanism), the box and the text scale together, so initials stay fully visible.
+
+**Manual testing steps**
+
+1. In a UI with letter avatars, set browser zoom to 200% (Ctrl or Cmd and +) and confirm the box and initials scale together with nothing clipped.
+2. Optional: compare against text-only resize (Firefox "Zoom Text Only") to see the px box not scaling; long initials there are an author concern.
+
+**Pass:** initials and fallback text stay fully visible at 200% page zoom. The container scales with the page.
+
+#### 1.4.5 Images of Text · AA
+
+`✅ Supports` · `◐ Shared`
+
+- Letter/initials children and the `alt[0]` fallback are live, CSS-styled DOM text, not images of text.
+- The only risk is an author deliberately passing a bitmap of text as the avatar image.
+- Confirmed by a unit test in [`./Avatar.test.js`](./Avatar.test.js).
+
+**Manual testing steps**
+
+1. Select a letter avatar's initials with the mouse (or zoom in) and confirm they behave like real text (selectable, stay crisp), not an image.
+
+**Pass:** avatar initials and fallback are real text; the component renders no image of text.
+
+#### 1.1.1 Non-text Content · A
+
+`✅ Supports` · `◐ Shared`
+
+- When given an `src`/`srcSet`, the component renders a native `
` and forwards the author's `alt` as its text alternative; the states without images (`Person` fallback and `SvgIcon` children) are `aria-hidden`, so assistive technology skips them as decorative.
+- A text alternative is the author's responsibility.
+- An icon-only avatar (`IconAvatars`) or the `Person` fallback exposes nothing to assistive technology by default; if the icon carries meaning, the author must add an accessible name.
+- axe-core `image-alt` confirms a text alternative is present on the image avatar.
+
+**Manual testing steps**
+
+1. With a screen reader running (VoiceOver with Safari, or NVDA with Chrome), focus an image avatar (`src` and `alt`) and confirm it renders a native `
` whose `alt` is announced.
+2. For any informative image avatar in the product, confirm it has a meaningful `alt`; without one, a screen reader may announce the image filename.
+3. Confirm an icon-only avatar and the `Person` fallback are `aria-hidden` and silent.
+4. Confirm a decorative image avatar with `alt=""` is ignored by assistive technology.
+
+**Pass:** every informative avatar exposes a text alternative (`img` `alt` or an author-provided label); decorative and no-image avatars are silent. The component does its part; an informative avatar with no `alt` is an authoring failure.
+
+#### 1.3.1 Info and Relationships · A
+
+`✅ Supports` · `◐ Shared`
+
+- The only relationship (image-to-name) is conveyed natively via `
` and `alt` text.
+- If an author builds meaning by composing the avatar with surrounding content (for example, a name and avatar in a list item), conveying that structure is the author's responsibility.
+- Confirmed by unit tests in [`./Avatar.test.js`](./Avatar.test.js).
+
+**Manual testing steps**
+
+1. Inspect an image avatar in the accessibility tree (in Chrome DevTools: Elements panel, Accessibility tab): confirm the `img` exposes `role="img"` with the `alt` as its name.
+2. Confirm a letter or icon avatar's root div is generic (no spurious role) and its decorative icon is `aria-hidden`.
+3. Confirm no grouping, heading, or list semantics are implied by the avatar alone that would need programmatic encoding.
+
+**Pass:** the image-to-name relationship is programmatically available via native `img` and `alt`.
+
+#### 1.4.1 Use of Color · A
+
+`🚩` · `✅ Supports` · `◐ Shared`
+
+- Color is decorative only: the default gray background and the hash-derived `stringToColor` background in `BackgroundLetterAvatars` are aesthetic, so the component never uses color alone to convey meaning.
+- The avatar's information is carried by the image, initials, or icon, not by the background hue, so nothing is lost when color is removed.
+- If an author repurposes the background to encode status or role (for example, green for online), they must add a non-color cue (text, icon, or shape) to satisfy this criterion.
+
+**Manual testing steps**
+
+1. In a UI with color-backed avatars, turn on a grayscale view (in Chrome DevTools: Rendering tab, Emulate vision deficiencies) and confirm the identifying content is the initials or icon, not the hue.
+2. Confirm no information is lost when color is removed, and that the component does not use color alone to distinguish any state.
+
+**Pass:** no information conveyed by the avatar is lost in grayscale; any color-coded meaning an author adds is paired with a non-color cue.
+
+#### 1.4.3 Contrast (Minimum) · AA
+
+`⚠️ Partially Supports` · `◐ Shared`
+
+- Default letter avatars fail: `colorDefault` sets the text color to `background.default` (`#fff` in the light theme) on `backgroundColor` `grey[400]` `#bdbdbd`, about `1.9:1`. The children text is 20px at the inherited weight 400, so it is not WCAG large text and the `4.5:1` threshold applies; at `1.9:1` it fails even the `3:1` large-text threshold. This affects `H`, the `alt[0]` fallback, and any default-palette letter avatar.
+- Colored demos fail too: `LetterAvatars` renders white "N" on `deepOrange[500]` `#ff5722` = `3.16:1`, below `4.5:1`; only its white "OP" on `deepPurple[500]` passes, at `7.3:1`.
+- `BackgroundLetterAvatars` compounds the risk: `bgcolor` is an arbitrary `stringToColor` hash with white text, so contrast is unguaranteed. Of its three names, "Tim Neutkens" (`#fd7e97`) is `2.44:1` (below even the `3:1` min) and "Jed Watson" (`#1f6cfa`) reaches only `4.58:1`.
+- A working photographic `
` is exempt (it's not text), and the dark-theme default (`grey[600]` `#757575` on `#121212`, about `4.1:1`) is also below `4.5:1`. Icons are non-text and are out of scope here. Authors must override `bgcolor`/`color` to reach `4.5:1` for letter avatars.
+- axe-core `color-contrast` flags the low-contrast letter demos.
+
+**Manual testing steps**
+
+1. With a contrast tool (the color picker in browser DevTools shows a ratio), measure a default gray letter avatar's initials against its background: expect about 1.9:1.
+2. Confirm the initials are 20px at weight 400 (not large text), so `4.5:1` applies.
+3. Measure letter avatars on custom `bgcolor` values (for example `deepOrange[500]`, or a `stringToColor`-derived color) against the white text.
+4. Confirm a working image avatar is exempt (no text on background).
+5. Repeat in the dark theme (`grey[600]` on `#121212`).
+
+**Pass:** all letter and fallback text meets `4.5:1` (or `3:1` if it's sized to qualify as large text). The default gray letter avatar, the `deepOrange[500]` example, and arbitrary `stringToColor` backgrounds do not meet this without author color overrides.
+
+#### 1.4.11 Non-text Contrast · AA
+
+`🚩` · `⚠️ Partially Supports` · `◐ Shared`
+
+- Meaningful icon-child avatars fall short: when an icon is the avatar's sole, unlabeled content (`IconAvatars`), it is a graphical object required to understand the content, so it needs `3:1`. The component's `colorDefault` renders a white icon on `grey[400]`/`#bdbdbd` (`1.9:1`), and author backgrounds vary, for example `green[500]`/`#4caf50` = `2.78:1` fails, while `pink[500]`/`#e91e63` = `4.35:1` passes. So the default and low-contrast author backgrounds are below `3:1`.
+- Exemptions: the colored circle or square is a decorative graphical object, and the `Person` fallback is a decorative placeholder. The default avatar is non-interactive so the user-interface-component part of this criterion does not apply.
+- Under `@media (forced-colors: active)` the component sets `1px solid ButtonBorder`, so the shape stays visible in Windows High Contrast Mode.
+- A meaningful icon avatar needs both a `≥3:1` foreground/background and an accessible name (1.1.1, since icon children default to `aria-hidden`).
+- Rated Partially Supports because the `1.9:1` shortfall comes from the component's own `colorDefault`, whereas the missing name is an author responsibility.
+
+**Manual testing steps**
+
+1. In a UI with icon avatars on different backgrounds, measure each icon against its background: a white icon on the default gray (about 1.9:1) or on `green[500]` (2.78:1) falls below 3:1.
+2. Confirm the colored circle or square itself is decorative, not an information-bearing graphic.
+3. Enable Windows High Contrast / forced-colors and confirm the `1px` `ButtonBorder` keeps the avatar boundary visible.
+
+**Pass:** any icon that conveys meaning meets `3:1` against its background; the decorative container shape and `Person` placeholder are exempt, and the boundary stays visible in forced-colors mode.
+
+### ⚙️ Automated
+
+#### 1.4.10 Reflow · AA
+
+`✅ Supports` · `● Component`
+
+- The avatar is a fixed 40px box that establishes no horizontal layout, so at a 320 CSS pixel width (or 400% zoom) it cannot force two-dimensional scrolling; it scales and wraps with its container. Covered by a Playwright test at a 320px viewport.
+
+#### 1.4.12 Text Spacing · AA
+
+`✅ Supports` · `● Component`
+
+- An avatar holds 1 to 2 character initials (or the `alt[0]` fallback) in a fixed 40px box with `overflow: hidden`, so the only text-legibility risk is clipping. The four spacing overrides do not enlarge one or two initials enough to overflow the box, so nothing is clipped.
+- Confirmed by a Playwright regression test in `test/regressions/index.test.js`. Covered by a Playwright test applying the WCAG text-spacing overrides.
+
+## Not applicable
+
+- **1.3.5 Identify Input Purpose (AA).** Not an input; collects no user information.
+- **1.4.13 Content on Hover or Focus (AA).** Shows no hover or focus content; a `Tooltip` wrapper would own this.
+- **2.4.4 Link Purpose (In Context) (A).** Renders no anchor or `role="link"` by default; an author who sets `component="a"` owns the link's purpose.
+- **2.5.3 Label in Name (A).** Not a labeled control; the `img`'s `alt` is an accessible name, not a visible label.
+- **3.1.2 Language of Parts (AA).** Names and initials are an explicit exception; other text is author-supplied.
+- **4.1.2 Name, Role, Value (A).** Applies to controls; the static `div`/`img` exposes no control role, state, or value (the `img`'s `alt` is covered by 1.1.1).
+- **4.1.3 Status Messages (AA).** Adds no live region; the image-to-fallback swap is not a status message.
+- **Time-based media (1.2.1 to 1.2.5).** No audio or video.
+- **Audio Control (1.4.2).** Emits no audio.
+- **Orientation (1.3.4).** Sets no orientation lock; a layout concern.
+- **Keyboard, pointer, and focus (2.1.1, 2.1.2, 2.1.4, 2.4.3, 2.4.7, 2.4.11, 2.5.1, 2.5.2, 2.5.4, 2.5.7, 2.5.8, 3.2.1, 3.2.2).** The default root is a non-interactive `
` with no role, `tabIndex`, or handlers, so it is not focusable and takes no keyboard, pointer, or motion action. An author who makes it interactive owns these.
+- **Timing and motion (2.2.1, 2.2.2, 2.3.1).** Sets no timer (`useLoaded` waits on load and error events), and renders nothing that moves, blinks, or flashes.
+- **Page and site structure (2.4.1, 2.4.2, 2.4.5, 2.4.6, 3.1.1, 3.2.3, 3.2.4, 3.2.6).** Document title, page language, navigation, headings, and cross-page consistency are the author's concern in their site or app.
+- **Input and errors (3.3.1, 3.3.2, 3.3.3, 3.3.4, 3.3.7, 3.3.8).** The avatar collects, validates, and submits no input.
+
+## Level AAA
+
+- **1.4.6 Contrast (Enhanced), 7:1.** Default letter avatars already fail the AA 4.5:1 (white on `grey[400]`, about 1.9:1), so they fall well short of 7:1. `🚩`
+- **1.4.9 Images of Text (No Exception).** Initials and the `alt[0]` fallback are live DOM text, so the component meets even the no-exception form by default; only an author-supplied bitmap of text would fail. `🚩`
+- Also touched, in the same shape as their A and AA siblings: **1.4.8 Visual Presentation, 1.3.6 Identify Purpose.** `🚩`
+
+## Scope and test environment
+
+- **Standard.** WCAG 2.2, Level A and AA.
+- **Component version.** `@mui/material` 9.1.2.
+- **Scope.** The Avatar component in isolation, rendered through its documented API. `AvatarGroup` is a separate component and is out of scope.
+- **Automated.** axe-core via the Playwright regression harness (results in [`avatars.a11y.json`](../../../../docs/data/material/components/avatars/avatars.a11y.json)), a text-spacing clip test in the same harness, and unit tests in `Avatar.test.js`.
+- **Assistive-technology review.** Spot checked but not audited. `🚩` criteria are assessed from source pending a review with NVDA, JAWS, and VoiceOver.
diff --git a/packages/mui-material/src/accessibility.md b/packages/mui-material/src/accessibility.md
new file mode 100644
index 00000000000000..79dcb4f009bdf9
--- /dev/null
+++ b/packages/mui-material/src/accessibility.md
@@ -0,0 +1,68 @@
+# Accessibility conformance reports
+
+Each component is rated against WCAG 2.2 Level A and AA and documented at `/accessibility.md`.
+
+## The status line
+
+For each SC this indicates:
+
+1. How well it conforms
+2. Whether the library or user (the author) is responsible for conformance
+
+```text
+ ·
+```
+
+For example this:
+
+```text
+⚠️ Partially Supports · ● Component
+```
+
+Means:
+
+1. Partially conforms
+2. The component is fully responsible for WCAG conformance
+
+## Conformance
+
+Whether the component meets the applicable Success Criterion. [VPAT](https://www.itic.org/policy/accessibility/vpat) terminology is used:
+
+| Symbol | Term | Description |
+| :----- | :----------------- | :---------------------------------------------- |
+| ✅ | Supports | Met, no known defects. |
+| ⚠️ | Partially Supports | Some functionality fails. |
+| ❌ | Does Not Support | Most functionality fails. |
+| ➖ | Not Applicable | The criterion does not apply to this component. |
+
+A criterion is **flagged** (`🚩`, shown first in its status line) when its rating is assessed from the component's source but not yet confirmed by a test or recorded review. The flag concerns evidence, not conformance, and does not imply a defect.
+
+## Responsibility
+
+Whether the responsibility for meeting conformance is on the library, the author (library user), or shared.
+
+| Symbol | Term | Description |
+| :----- | :-------- | :---------------------------------------------------------- |
+| ● | Component | Satisfied on its own. |
+| ◐ | Shared | Satisfied when the component is used as documented. |
+| ○ | Author | Depends on your implementation and the surrounding content. |
+
+## Testing-method groups
+
+Criteria are grouped by testing method, and roughly sorted by descending order of "human judgement required".
+
+| Symbol | Group | What it takes |
+| :----- | :-------- | :-------------------------------------------------------------------------------------- |
+| 🔍 | Manual | Human, visual, or assistive-technology judgment. |
+| 🔁 | Hybrid | Automation catches regressions; judgment still needed. |
+| ⚙️ | Automated | A deterministic test proves it. `🚩` means such a test is feasible but not yet written. |
+
+## Scope
+
+Components are rated in isolation against WCAG 2.2 A and AA. The levels are [cumulative](https://www.w3.org/WAI/WCAG2AA-Conformance), that is, AA includes all of A.
+
+## Reports
+
+| Component | ✅ Supports | ⚠️ Partially Supports | ❌ Does Not Support | ➖ Not Applicable |
+| :---------------------------------- | :---------- | :-------------------- | :------------------ | :---------------- |
+| [Avatar](./Avatar/accessibility.md) | 9 | 2 | 0 | 44 |
diff --git a/test/regressions/demoMeta.ts b/test/regressions/demoMeta.ts
index cc224b1536b949..1395751183f545 100644
--- a/test/regressions/demoMeta.ts
+++ b/test/regressions/demoMeta.ts
@@ -178,6 +178,20 @@ export const SCREENSHOT_RULES: ScreenshotRule[] = [
* Initial PR scope: `buttons` only. Other components onboard incrementally.
*/
export const A11Y_RULES: A11yRule[] = [
+ {
+ test: 'docs/data/material/components/avatars/{LetterAvatars,BackgroundLetterAvatars,IconAvatars,VariantAvatars,AvatarA11yImage}',
+ enabled: true,
+ },
+ // Avatar's default `colorDefault` styling is white text on grey[400] (~1.9:1),
+ // and the documented letter/background examples use low-contrast author
+ // colors, so color-contrast genuinely fails (WCAG 1.4.3). Record the
+ // violations in the JSON without failing the build. IconAvatars (icons only,
+ // aria-hidden, no text) is excluded here so it still asserts a clean pass.
+ {
+ test: 'docs/data/material/components/avatars/{LetterAvatars,BackgroundLetterAvatars,VariantAvatars,AvatarA11yImage}',
+ enabled: true,
+ skipAssertions: ['color-contrast'],
+ },
{
test: 'docs/data/material/components/buttons/{BasicButtons,ColorButtons}',
enabled: true,
diff --git a/test/regressions/index.test.js b/test/regressions/index.test.js
index 542e1d4487a5f0..649dbbdf5d3b2f 100644
--- a/test/regressions/index.test.js
+++ b/test/regressions/index.test.js
@@ -362,6 +362,44 @@ async function main() {
});
});
});
+ describe('Avatar', () => {
+ // Deterministic clip check for 1.4.12 Text Spacing, which axe cannot cover.
+ // Renders `LetterAvatars` and targets the two-character ("OP") avatar,
+ // whose fixed 40px box with `overflow: hidden` is the only clipping risk.
+ test('1.4.12 Text Spacing: initials stay visible under the WCAG overrides', async ({
+ pooled,
+ }) => {
+ const { page } = pooled;
+ await renderFixture(page, '/docs-components-avatars/LetterAvatars');
+ const clipped = await page.evaluate(() => {
+ const style = document.createElement('style');
+ style.textContent =
+ '* { line-height: 1.5 !important; letter-spacing: 0.12em !important; word-spacing: 0.16em !important; }';
+ document.head.appendChild(style);
+ const avatar = Array.from(document.querySelectorAll('.MuiAvatar-root')).find(
+ (node) => node.textContent === 'OP',
+ );
+ if (!avatar) {
+ throw new Error('LetterAvatars no longer renders an "OP" avatar');
+ }
+ const range = document.createRange();
+ range.selectNodeContents(avatar);
+ const text = range.getBoundingClientRect();
+ const box = avatar.getBoundingClientRect();
+ style.remove();
+ return (
+ text.left < box.left - 0.5 ||
+ text.right > box.right + 0.5 ||
+ text.top < box.top - 0.5 ||
+ text.bottom > box.bottom + 0.5
+ );
+ });
+ if (clipped) {
+ throw new Error('Avatar initials are clipped under WCAG text-spacing overrides');
+ }
+ });
+ });
+
registerCssLayoutSuites({ test, renderFixture, routes });
});
}