Alt text is usually filed under accessibility, and usually addressed the way compliance work is addressed: once, under pressure, page by page, until the audit tool stops complaining. For an agent it is something simpler. Software that fetches your HTML does not download your images. The alt attribute is the only description of each image it will ever receive, and an image without one is a hole in the page's text where a product, a chart or a person used to be. Check B6 measures the size of that hole: two points, rated Low effort, awarded in full when at least 80% of content images carry a non-empty alt. This post covers what the scanner counts, what the empty-alt convention does to the number, and why the efficient audit starts from where images come from.
What the scanner counts
The scanner collects every img element across every crawled page that returned a 200 with a body, and pools them. It then drops any image that declares itself an icon: an img whose width attribute or height attribute parses as a number below 50 is excluded. That is the whole test. It reads the attributes on the element and nothing else. An icon with no dimension attributes counts as a content image. So does one sized only in CSS, and so does one with width="100%", since that is not a number. Images that are not img elements are not counted: inline svg, CSS background images and anything drawn on a canvas are outside the check.
An image is counted as having alt text when the attribute is present and non-empty after trimming. A missing attribute, alt="" and alt=" " all count the same way: no alt.
Coverage is the number with alt divided by the total, pooled across the crawl rather than averaged page by page. A site with no content images at all scores the full two points, with contentImages: 0 in the evidence; there is nothing to caption. Otherwise the bands are:
| Coverage | Points |
|---|---|
| 80% or above | 2 |
| 50% up to 80% | 1 |
| Below 50% | 0 |
The evidence block records contentImages, withAlt and coverage, the last rounded to two decimal places.
Pooling has two consequences. A product listing with sixty images weighs sixty times as much as an about page with one, so coverage is effectively a measure of your highest-volume template. And because icons only leave the count when their dimensions say so, payment logos, social icons or rating stars rendered as img elements without width and height drag coverage down with images nobody would think to describe. Declaring intrinsic dimensions on those fixes the count, and it is the right markup anyway.
Decorative images and the empty alt
WCAG 2.2, success criterion 1.1.1, asks that non-text content have a text alternative serving the same purpose, and that purely decorative content be implemented so assistive technology can ignore it. In HTML the way to do the second is alt="". The W3C's images tutorial walks through deciding which an image is.
The scanner counts alt="" as no alt. That is deliberate, and it is a limitation. Deliberate because the check measures the information a page's images contribute to a machine, and an empty alt contributes none by design. A limitation because an empty alt someone chose and an empty alt a template emitted by default are identical in the HTML.
The wrong response is to write alt text on decorative images. That makes the page worse for a screen reader user, who now hears a description of a divider, and adds noise to the text a machine extracts. The right responses, in order: check whether the image is really decorative, since a product photo, a team portrait, a chart or a screenshot is not, and the empty alt usually means nobody filled the field. If it is decorative and small, give it width and height under 50 and it leaves the count. If it is decorative and large, a background image or an inline svg does the same job outside the count. For whatever remains, the threshold is 80% rather than 100%, and the margin is where genuinely decorative images belong.
Audit the sources, not the pages
Every img on a site comes from one of a small number of places, and each place is fixed once. Auditing page by page finds the same missing alt on every product page in turn; auditing the source finds it once, in the line that generates it.
Template images. The logo, the homepage hero, badges and partner logos in the footer. They live in a layout file, and a fix there applies to every page. The logo's alt is the organisation's name, which is also the name D5 expects to see agree across the site.
Component loops. Product cards, article cards, a team grid, a gallery. One line in one component renders every image of that kind:
<img src="{{ product.image }}" alt="{{ product.image_alt | default: product.title }}" width="600" height="600" />
The failure to look for is alt="{{ product.image_alt }}" with no fallback, over a field that is empty for most records. Falling back to the product or article title is a single change and moves coverage more than anything else on this list.
The CMS media library. In most systems the alt lives on the asset rather than the page: WordPress's alternative text field on the media item, Shopify's alt on each product image. Export the library or the product CSV, filter for an empty alt column, fill it, and re-import. Check how your platform treats the field at insertion time: some copy the library alt into the page when the image is placed, so later edits do not flow back, and as of this writing the answer differs between systems.
Rich text bodies. Images an author dropped into an article. In a repository of Markdown they are easy to find:
grep -rnE '!\[\s*\]\(' content/
For a database-backed editor, or any site at all, a plain fetch of the rendered page does the same job, which is what the scanner does:
curl -sL https://example.com/products/ -o page.html
python3 - <<'PY'
import re
html = open("page.html", encoding="utf-8", errors="replace").read()
for tag in re.findall(r"<img\b[^>]*>", html, flags=re.I):
alt = re.search(r'\balt="([^"]*)"', tag, flags=re.I)
if alt is None or not alt.group(1).strip():
print(tag[:160])
PY
Run it against one page per template rather than every page; after the first few unlabelled images the source is obvious. The work splits cleanly: an engineer fixes the loops and templates, a content team fills the library, and neither waits on the other. If you would rather have the list of sources handed over with the fixes applied, that is what the implementation service does.
What a good alt says to a machine
An alt is the text the image contributes to the page. Write what a reader who cannot see the image needs to know in this context, and nothing else. MDN's description of the attribute puts it the same way.
For a product image that is the product, the variant and the view: Field notebook, A5, dot grid, olive cover, front. Not product image, not the file name, not a string of search terms. For a chart it is the finding, not the chart type: Monthly signups, January to June 2026, rising from 120 to 410. For a screenshot it is what is shown: Settings page with the API keys tab selected. For a portrait it is the name and role. Leave out "image of" and "picture of", since the element already says that.
The check measures coverage, not quality, so a file name in every alt would pass it. It would also fail the purpose: a retrieval system that indexes your page text now holds IMG_2041.jpg where it should hold the fact that you sell an olive notebook, and a model asked whether you do will not find out from the image. The alt is the only place that fact lives once the image is stripped away. That is what makes it data.
Keep it to a sentence. Anything longer belongs in the surrounding prose or a figcaption, where it also raises the text side of the clean text ratio. None of this is more than hours of work, which is why the rubric rates B6 Low, in the sense the three tiers of fixes gives that word.
Check your own site
Run the free scan and read contentImages, withAlt and coverage under B6, then run the snippet above against the template that produces most of your images. The definition of the check, and the icon exclusion, are on the methodology page.