Fix
Images without alternative text
A photograph with no text alternative is invisible to anyone who cannot see it — and in a shop, the photograph is often the only thing naming the product. This is WCAG 2.1 success criterion 1.1.1, Level A: about as basic as the standard gets.
Who this locks out
Product images with no alt text are invisible to anyone using a screen reader.
- Screen reader customers browsing the product grid
- Blind and low-vision customers checking the basket
- Voice-control users who name what they want to click
- Anyone whose images fail to load on a slow connection
The transform
Before
<img src="/wp-content/uploads/2026/03/skillet-26.jpg" class="wp-post-image"> After
<img src="/wp-content/uploads/2026/03/skillet-26.jpg" class="wp-post-image" alt="Cast-iron skillet, 26 cm, matt black"> Detection
How this is tested here
Every scan runs three independent engines and nine custom probes against your rendered pages, and records which engine flagged what. These are the real rules behind this fix family.
| Criterion | Level | Detected by | What we do |
|---|---|---|---|
| 1.1.1 Non-text Content | A | axe-core (aria-meter-name, aria-progressbar-name, image-alt, input-image-alt) · IBM Equal Access (applet_alt_exists, application_content_accessible, area_alt_exists) | auto |
This family is keyed to the axe rules image-alt</code>, <code>input-image-alt</code>, <code>role-img-alt in the
engine's own category map.
What Klarvo Access does
Klarvo drafts the alt text from the image and its surrounding context, has a second, stricter model audit the draft, applies what passes server-side in the HTML your store serves, then re-checks the served page independently to prove the text is really there.
Auto-applied
Generated, then applied server-side in the HTML your store actually serves — and independently re-validated in the served page before it counts as resolved.
An automated finding is evidence, not a certificate. Conformance is certified only by the professional audit — how that works.
Do it yourself
Fixing this by hand
What breaks, and who it locks out
Sighted, your shop page is a grid of photographs with prices beneath them. Read aloud with the alt attributes missing, it is a run of unnamed links: “link, link, link”. Some screen readers fall back to the file name, spelled out letter by letter where you meant “Cast-iron skillet, 26 cm”. Nothing there says what is for sale.
It gets worse the closer you get to the money.
- Product page. Colour is often carried by swatch thumbnails alone. Unnamed, the choice between “Sage” and “Clay” is unavailable to anyone who cannot see them.
- Basket. An unnamed line-item thumbnail leaves a customer with two similar jackets unable to tell which row’s remove button belongs to which coat — a returns problem as much as an access one.
- Search. Results are the same grid with the same silence, and search is disproportionately how customers with access needs shop.
- Checkout. Trust badges, delivery icons and card logos live here. When the words are baked into the artwork (“free returns within thirty days”), that promise does not exist for part of your audience — at the moment they decide whether to trust you with a card number.
Two groups are easy to forget: voice-control users click by saying a thing’s name, so an unnamed image link cannot be clicked at all; and when a photograph fails to load, the alt text appears in its place.
How Klarvo detects it
One criterion: WCAG 2.1 SC 1.1.1 Non-text Content, Level A.
Three named axe-core rules (4.12.0) own this family:
image-alt— an<img>with no alt and no other accessible name.input-image-alt— an<input type="image">submit button with no alt, so the button has no name at all.role-img-alt— an element carryingrole="img"(an inline SVG, an icon span) with nothing to announce.
IBM Equal Access reaches the same criterion through its own independent rules — img_alt_null, img_alt_valid, img_alt_misuse, img_alt_redundant, imagebutton_alt_exists, svg_graphics_labelled — and HTML_CodeSniffer’s tests reference it too. Two engines built by different teams agreeing that an image has no name is far stronger than a single unchallenged result; where only one names it, the report says so. There is no bespoke Klarvo probe here, and none is needed.
Detection runs against the page as a browser renders it, so images a script injects are seen too. The fix, though, is applied in the raw HTML your server sends — the only surface a server-side rewrite can reach — so images that exist only after JavaScript runs are routed to a person rather than quietly dropped.
What Klarvo does about it
This family is auto tier: the alt text is generated, applied server-side in the HTML your store serves, and then independently re-validated.
The sequence is built to distrust itself. Klarvo gathers the image and its context — surrounding copy, the link it sits inside, the product it belongs to — and a vision model drafts a candidate. A deterministic filter discards anything filename-shaped, too short, or drawn from a stop list of boilerplate (“image”, “photo”, “product”). What survives goes to a second, stricter model on a fresh prompt, judging objective failure modes: right kind of image (decorative, informative, functional), true of the picture, no “image of”, sane length, not a repeat of adjacent text.
Only a clear pass is applied, as a targeted rewrite of that one tag, matched by a fingerprint of the served opening tag — never a script layered over your theme. Anything doubtful is queued for a person to approve, which is why a store full of awkward images gets an honest partial result instead of a clean-looking lie.
Klarvo then checks its own work from the outside: it re-fetches the served page to confirm the alt is present on that image, re-renders and re-runs image-alt to confirm no violation remains, and re-audits the live value against the image. Every draft, verdict, route and outcome goes into an append-only record.
How to fix it yourself
Decide what each image is
The W3C’s alt decision tree is the reference; in a shop it comes down to four cases.
- Informative — the main product shot. Use the name a customer would use:
alt="Cast-iron skillet, 26 cm, matt black". Not the SKU, not the file name. - Functional — the image is the control: the logo that goes home, the magnifier inside the search button. Describe the action:
alt="Search products", neveralt="magnifying glass". - Decorative — a flourish, a divider, a thumbnail inside a link that already carries the product name as text. Give it
alt="": present and empty, with no title attribute. - Complex — a size chart or care diagram. Alt text cannot carry a table; put the information on the page as text and keep the alt short.
Set the alt in the Media Library once
WordPress reads an attachment’s alt from the _wp_attachment_image_alt meta field, with no fallback to the caption or the title. Leave the Alt Text field blank and every template emits alt="", which tells assistive technology the image is decorative — which is why many stores that “have alt text” still fail. Because WooCommerce renders product images through wp_get_attachment_image(), filling that one field fixes the grid, the product page, the basket and the order emails together. Library alt becomes the default at insert; editing it inside a block changes only that post.
Find the gaps with WP-CLI, then write real text for each:
wp post list --post_type=attachment --post_mime_type=image --format=ids \
| tr ' ' '\n' \
| while read -r id; do
[ -z "$(wp post meta get "$id" _wp_attachment_image_alt 2>/dev/null)" ] && echo "$id"
done
wp post meta update 4127 _wp_attachment_image_alt "Cast-iron skillet, 26 cm, matt black"
Resist looping the product title into every gallery shot: six images announcing “Cast-iron skillet” is a new problem, not a fix.
A stopgap for a large catalogue
In a child theme’s functions.php, fall back to the parent product’s title where alt is genuinely missing:
add_filter(
'wp_get_attachment_image_attributes',
function ( $attr, $attachment ) {
if ( ! empty( $attr['alt'] ) ) {
return $attr;
}
$parent = (int) $attachment->post_parent;
if ( $parent && 'product' === get_post_type( $parent ) ) {
$attr['alt'] = get_the_title( $parent );
}
return $attr;
},
10,
2
);
A floor, not a finish: every photograph of a product gets the same name.
Images hard-coded in templates
Copy the template into a child theme first, then fix the markup:
<a href="/"><img src="/wp-content/themes/your-child/img/logo.svg" alt="Northwood Kitchenware, home"></a>
<input type="image" src="/wp-content/themes/your-child/img/search.png" alt="Search products">
Where an icon sits inside a button or link that already has text, empty the icon’s alt and let the control carry the name — see empty buttons and empty links.
Block themes and WooCommerce Blocks
Select the image and use Content → Alternative text in the block sidebar. Three traps: the Site Logo block takes its alt from the media entry, so fix it in the library; Cover blocks render their image with an empty alt unless you fill the block’s own Alternative text field — and a fixed-background Cover, like any CSS background set by the theme, carries no alt at all, so words baked into that artwork must also exist as page text; the Product Image block in a Query Loop pulls the attachment’s alt, so the library fix flows through by itself. In the block checkout, payment icons come from the gateway — classic gateways build theirs through the woocommerce_gateway_icon filter, so you can rewrite that markup; icons drawn client-side must be fixed in the extension.
How you know it worked
Check the HTML your server sends, not the page in front of you:
STORE="$(wp option get home)"
curl -s "$STORE/shop/" | grep -o '<img[^>]*>' | grep -v ' alt='
No output means every <img> on that page carries an alt. Repeat for a product page, the basket, the checkout and a search results page — the pages that sell, not the homepage alone.
Then verify with more than one pair of eyes. Re-run an axe-based extension and a second, independent checker: agreement is the signal, disagreement is worth a look by hand. Open the VoiceOver rotor’s image list, or press G in NVDA’s browse mode to step through the graphics — the set should read like a catalogue of products, not a directory of files, and decorative images should announce nothing. Re-check after every product import and theme update: alt lives in content, so it drifts — and a person still has to judge whether the names that remain are true.
Sources: W3C WAI — Understanding SC 1.1.1: Non-text Content · W3C WAI — An alt Decision Tree · W3C WAI — Images Tutorial: Functional Images · W3C — Technique H37: Using alt attributes on img elements · W3C — Technique H67: Using null alt text and no title attribute on img elements · W3C — Technique H36: Using alt attributes on images used as submit buttons · WordPress — Media Library screen (the Alt Text field) · WordPress — Image block (Alternative text)
FAQ
Questions this raises
Will Klarvo change my product photos or my Media Library?
No. The image files and the attachment records stay exactly as they are; the fix is an alt attribute added to the tag in the HTML your server sends, applied by the plugin. Deactivate the plugin and the page returns to its original markup.
Is machine-written alt text good enough to publish?
Sometimes, and only under supervision. A deterministic filter rejects filename-shaped, too-short and boilerplate values before any model sees them, an independent second model audits the rest, and only a clear pass is applied — anything doubtful is queued for a person. After it goes live, the served value is audited again and flagged if it no longer holds up. An automated finding is evidence, not a certificate.
Do decorative images need alt text too?
They need an empty alt attribute, which is not the same as no attribute. alt="" tells assistive technology to skip the image; a missing alt leaves it guessing, and some screen readers fall back to reading the file name aloud.
Find out whether your store has this one.
The free scan checks your real pages — home, product, populated basket, hydrated checkout — with all three engines and all nine probes, and tells you exactly what it found and where.