# AI alt text that misses or misreads what a photo shows

Source: https://easeweb.dev/learn/generated-alt-text
Topics: Images and media > Alt text written by AI says what the image is there to show (WCAG 1.1.1)
The fix: Keep the AI's description as a draft, never as the alt text. Show it beside the photo in the editor, marked as an AI draft, and publish a photo only once a person who knows the product has checked or rewritten its description.
Test with: screen reader
References: https://www.w3.org/WAI/WCAG22/Understanding/non-text-content.html https://www.w3.org/WAI/tutorials/images/decision-tree/ https://www.w3.org/WAI/tutorials/images/informative/ https://support.microsoft.com/en-us/office/everything-you-need-to-know-to-write-effective-alt-text-df98f884-ca3d-456c-807b-1a1fa82f5dc2 https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/img#authoring_meaningful_alternate_descriptions

This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.

## What happens

A stationery shop sells a weekly planner for 2027 in four colours. Its product page has three photos: the sage cover, the planner open at a week, and the four colours side by side. The shop's media library has an AI alt text add-on. When a photo is uploaded, it sends the photo to an image description service and writes what comes back into the photo's alt text. Nobody opens the field, and the page goes live.

A screen reader user moves through the photos and hears "A green notebook on a white table, image", then "An open notebook with lines on the pages, image", then "Three notebooks in green, blue and pink, image". None of them is a file name or the word "image", and each sounds careful. The first two name what is in the photo and leave out why it is there: that this is the 2027 planner, and that it shows a week across two pages, with a box for notes. The third is wrong. There are four planners, and their colours are sage, navy, terracotta and sand. A shopper who would like the terracotta one hears that it comes in pink, and never hears of sand.

## Why it fails

Success criterion 1.1.1 Non-text Content asks that an image that carries information has a text alternative that serves the same purpose. These photos are there to sell the planner: to show its cover, its layout and its colours. The description service gets the pixels and nothing else. It doesn't know the product, the page, or that the third photo is where shoppers choose a colour, so it names the objects it recognises, and where it misreads the photo, the text alternative says something the photo doesn't. A sentence that leaves out what the image is there to show, or gets it wrong, doesn't serve the same purpose, however fluent it is.

The service did what it was asked to do. The failure is in how its answer is used: it goes straight into the alt attribute, which the page publishes, with no step where a person who knows the product reads it. Microsoft's guidance for its own automatic alt text in Office is to review and edit what it generates, and to remove notes it adds such as "Description generated with high confidence". An add-on that writes into the alt field on upload leaves that review out.

## Who is affected

Alt text is also what shows when a photo fails to load, and what reading and translation tools get. In a shop, it is often the only way to learn what a product looks like before paying for it.

## What automation and AI miss

axe looks for an image with no alt attribute, and ANDI for alt text that contains a file name. These alts pass both: they are full sentences. No checker can compare a sentence with a photo and know which colours the shop sells. This audit sends a review lead for alt text that still carries a generator's own note, such as "Description generated with high confidence", since the note is meant to be deleted when a person reviews the text. Generated text without such a note looks like any other alt text. A second AI asked to check the first, given only the photo, sees the same pixels and knows no more about the product.

## Before: the AI's description written into the alt text on upload

```js media.js
// When a photo is uploaded to the media library
media.on('upload', async (photo) => {
  photo.alt = await describeImage(photo.url);
  await photo.save();
});

// When the editor's Publish button is pressed
app.post('/products/:id/publish', async (req, res) => {
  const product = await products.get(req.params.id);
  await product.publish();
  res.redirect(product.url);
});
```

```html editor.html
<form method="post" action="/products/42/publish">
  …
  <h2>Photos</h2>
  <ul class="photos">
    <li>
      <img src="planner-cover.jpg" alt="">
    </li>
    …
  </ul>
  <button type="submit">Publish</button>
</form>
```

```html product.html
<h1>Weekly planner 2027, A5</h1>
<div class="gallery">
  <img src="planner-cover.jpg" alt="A green notebook on a white table.">
  <img src="planner-inside.jpg" alt="An open notebook with lines on the pages.">
  <img src="planner-colours.jpg" alt="Three notebooks in green, blue and pink.">
</div>
```

- The upload handler writes the service's answer straight into the alt text, so it is published the moment the product is.
- The editor shows the photos but never their descriptions, so nobody reads them before Publish.
- The cover and inside photos are described by what is in them, without the year, the layout or the notes box.
- The colours photo is described wrongly: three notebooks instead of four, and pink instead of terracotta.

## After: an AI draft that a person checks before it is published

```js media.js
// When a photo is uploaded to the media library
media.on('upload', async (photo) => {
  // A draft for a person to check. The photo has no alt text until they have.
  photo.altDraft = await describeImage(photo.url);
  photo.alt = null;
  await photo.save();
});

// When the editor's Publish button is pressed
app.post('/products/:id/publish', async (req, res) => {
  const product = await products.get(req.params.id);
  for (const photo of product.photos) {
    if (!req.body[`checked-${photo.id}`]) return res.sendStatus(422);
    photo.alt = req.body[`alt-${photo.id}`].trim();
    await photo.save();
  }
  await product.publish();
  res.redirect(product.url);
});
```

```html editor.html
<form method="post" action="/products/42/publish">
  …
  <h2>Photos</h2>
  <ul class="photos">
    <li>
      <img src="planner-cover.jpg" alt="">
      <label for="alt-1">What should a shopper learn from this photo?</label>
      <p id="alt-1-hint">Draft by AI. Check it against the photo.</p>
      <textarea id="alt-1" name="alt-1" aria-describedby="alt-1-hint">A green notebook on a white table.</textarea>
      <label>
        <input type="checkbox" name="checked-1" required>
        I have checked this against the photo
      </label>
    </li>
    …
  </ul>
  <button type="submit">Publish</button>
</form>
```

```html product.html
<h1>Weekly planner 2027, A5</h1>
<div class="gallery">
  <img src="planner-cover.jpg" alt="The weekly planner in sage, with 2027 in gold on the cover and an elastic band to keep it closed.">
  <img src="planner-inside.jpg" alt="Inside, a week across two pages: Monday to Wednesday and a box for notes on the left, Thursday to Sunday on the right.">
  <img src="planner-colours.jpg" alt="The four colours: sage, navy, terracotta and sand.">
</div>
```

1. Save the service's answer as a draft in a field of its own, and set no alt text yet. Nothing the AI wrote can reach the page by itself.
2. Show each draft in the editor, in a text box beside its photo, labelled with the question the description has to answer.
3. Tie a short hint to the box saying the text is an AI draft to check against the photo.
4. Add a required box to tick for each photo. The browser won't send the form until every photo has been checked, and the server refuses to publish without them too.
5. On Publish, write what the person checked or rewrote into the alt text. An empty box they ticked becomes `alt=""`, for a photo that adds nothing.

This fixes the Before and passes 1.1.1, as long as the person checking can see the photos and knows the product. The draft still saves typing; what makes it a text alternative is that someone who knows why the photo is on the page has read it.

## Implementation decisions

**Every way generated text arrives.** The draft field has to cover each route by which a description service reaches the alt text, not only the upload button:

- A bulk run over the media library, which many alt text plugins offer for images with no alt. In WordPress, a decorative image's alt field is left blank, just like a forgotten one, so a bulk run describes the dividers and backgrounds too.
- A review screen with an Accept all button. It is a review in name only: one click publishes every draft unread.
- An import from a product feed, a spreadsheet or another shop, where the descriptions were generated somewhere else.
- A button that regenerates the description of one photo. It must write over the draft, never over alt text a person has checked.

**Context makes a better draft, not a checked one.** Some services take the product's name or the page's keywords along with the photo, and their drafts get closer. They still can't see which colours the shop sells or what a shopper is choosing between, and they still misread a photo now and then, so the draft is still a draft.

**One photo, several places.** A media library usually keeps one alt per image, but the same photo can be the product's colour chart on one page and a decorative banner on another. Let the description be set where the photo is placed, or overridden there: as the W3C's images tutorial explains, the right text depends on why the image is on that page.

**Decorative is a choice.** An empty alt is right for a photo that adds nothing the text doesn't say, and only a person can decide that. A generated description on a divider is noise, and an empty alt on the colours photo would hide the colours. See [decorative images read out as file names or repeated text](/learn/decorative-images).

**Words and data in the picture.** A photo of a size chart or a label needs its words in the alt text or, better, as text on the page. A service that reads text in images can misread numbers, just as it can colours. See [images of text instead of real HTML text](/learn/images-of-text).

## Verify the fix

1. Add a photo each way the shop allows: upload, bulk generation, import and regeneration. In each case the generated text is saved as a draft, and the photo still has no published alt text, or keeps the one a person had already checked.
2. Try to publish the product with one photo unchecked: the editor stops at that photo's box, and the server refuses the same request sent without it.
3. Ask someone who can see the photos and knows the product to read each published alt text beside its photo: every colour, count and word is right, and each says what the photo is there to show.
4. With a screen reader, move through the published photos: each is read as the checked description, with no generator's note such as "Description generated with high confidence".

Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after changing the description service, its settings or how photos are added.

## Limits

This is a starting pattern, not a guarantee for every application. `describeImage`, `media` and `products` stand in for your own services. This guide covers generated descriptions published unread. An image with no alt, or a file name in it, is covered in [missing alt text on images that carry information](/learn/informative-images), and an upload that never asks for a description at all in [image uploads that never ask for alt text](/learn/alt-text-prompts). Charts and graphs need more than one sentence of alt text and have a topic of their own. Browsers that describe images with no alt text themselves, such as Microsoft Edge, are the reader's own tool, not the page's text alternative, and don't make an image without alt text pass. Test with the people who will buy from the page.

## What screen readers do (tested by hand by EaseWeb)

`aria-describedby`: Gives an element a description, such as a hint or an error, from text elsewhere on the page.

| Screen reader | Browser and system | Tab to it | Down Arrow |
| --- | --- | --- | --- |
| JAWS | Chrome, Windows 11 | Not tested | Not tested |
| NVDA | Chrome, Windows 11 | Not tested | Not tested |
| JAWS | Edge, Windows 11 | Not tested | Not tested |
| NVDA | Firefox, Windows 11 | Not tested | Not tested |
| NVDA | Edge, Windows 11 | Not tested | Not tested |
| JAWS | Firefox, Windows 11 | Not tested | Not tested |
| VoiceOver | Safari, macOS | Not tested | Not tested |
| VoiceOver | Safari, iOS | Not tested | Not tested |
| TalkBack | Chrome, Android | Not tested | Not tested |

https://easeweb.dev/learn/screen-reader-support

---

Get this guide in your coding agent: MCP https://easeweb.dev/mcp (setup at https://easeweb.dev/services/developers)
