Fix
"Read more" fourteen times on one page
A screen reader can list every link on a page and read them out in order. It is one of the fastest ways to navigate a shop — and it collapses completely when nine of the links say the same three words.
Who this locks out
"Read more" ×14 tells a screen-reader user nothing about where any of them go.
- Screen-reader users navigating by a list of links rather than by reading the page
- Voice-control users, who cannot distinguish nine identical link names by speaking
- Anyone skim-reading, for whom "read more" carries no information either
The transform
Before
<a class="more-link" href="/product/ceramic-mug/">Read more</a> After
<a class="more-link" href="/product/ceramic-mug/" aria-label="Read more about the Ceramic Mug">Read more</a> 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 |
|---|---|---|---|
| 2.4.4 Link Purpose (In Context) | A | axe-core (area-alt, link-name) · IBM Equal Access (a_text_purpose) | auto |
What Klarvo Access does
Klarvo detects the ambiguous links, drafts a name from the surrounding context, and sends it to your review queue — this one is never applied automatically, because only you know what the link was meant to say.
Review queue
Generated with a proposed value, then queued for you to approve, edit or reject. It is never applied silently, because this one is a judgement call.
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 the criterion actually asks
WCAG 2.4.4 Link Purpose (In Context) is satisfied when the purpose of each link can be determined from the link text alone or together with its programmatically determined context — the sentence, list item, table cell or paragraph it sits in.
That “or” is doing a lot of work, and it is why this is a fix family that automation should not settle by itself. “Read more” following a heading that names a product does have determinable context. The same words in a footer column do not. A machine can identify the pattern; it cannot reliably judge the context.
The stricter Level AAA criterion, 2.4.9 Link Purpose (Link Only), removes the “or” entirely. If you want to be excellent rather than merely conformant, aim at that one — it is also simply better writing.
How this is detected here
No engine in the scan carries a named rule for ambiguous-but-present link text — axe’s link-name fires only when a link has no name at all, which is the sibling family. So this one is found by Klarvo’s own deterministic pass over the rendered page: the same generic phrase — “Read more”, “Details”, “Click here” — repeated across links that point at different destinations. Each finding becomes a drafted rewrite in your review queue. Nothing in this family is ever applied on the machine’s own judgement, and a draft that fails its own audit is discarded rather than proposed.
Where the ambiguity creeps in
- Product grids and archives. Themes append “Read more” or “View product” to every excerpt. Pull up the links list on a shop page and you get the same phrase repeated once per product.
- “Details”, “More info”, “Learn more”. Blog roll-ups, cross-sells and upsell blocks.
- Bare URLs. A pasted
https://…link is read out character by character by some screen readers, which is worse than an unhelpful phrase. - “Click here”. Still remarkably common in shipping and returns copy, and it names an action rather than a destination.
- Icon-plus-text combinations where the text is the same for every item and the icon carries the difference.
The specific damage is not that any individual link is confusing. It is that the list is destroyed, and the list is the navigation mechanism.
How to fix it yourself
First choice: change the visible text
The best fix helps everybody, including people who are simply skimming.
<?php
// wp-content/themes/<child-theme>/functions.php
// WooCommerce's loop "read more" button, given a real name.
add_filter( 'woocommerce_loop_add_to_cart_link', function ( $html, $product ) {
if ( $product->is_type( 'simple' ) && $product->is_purchasable() ) {
return $html; // "Add to basket" is already clear
}
return sprintf(
'<a href="%1$s" class="button product_type_%2$s">%3$s</a>',
esc_url( $product->get_permalink() ),
esc_attr( $product->get_type() ),
esc_html( sprintf( __( 'View %s', 'your-theme' ), $product->get_name() ) )
);
}, 10, 2 );
For the classic post excerpt link:
add_filter( 'the_content_more_link', function ( $link ) {
return sprintf(
'<a class="more-link" href="%1$s">%2$s</a>',
esc_url( get_permalink() ),
esc_html( sprintf( __( 'Read more: %s', 'your-theme' ), get_the_title() ) )
);
} );
There is a design objection to this, and it is a real one: long link text in a tidy grid looks untidy. Which brings us to the second choice.
Second choice: keep the visible text, extend the accessible name
<a class="more-link" href="/product/ceramic-mug/" aria-label="Read more about the Ceramic Mug">
Read more
</a>
The visible text stays two words; the accessible name is specific. The rule to hold to is that the accessible name contains the visible text, so that a voice-control user saying “click read more” still matches the control.
If you would rather not use ARIA, the same result comes from a visually hidden span, which has the advantage of translating with the rest of your content:
<a class="more-link" href="/product/ceramic-mug/">
Read more<span class="screen-reader-text"> about the Ceramic Mug</span>
</a>
What not to do
- Do not put the product name in a
titleattribute and consider it handled.titleis unreliable, invisible on touch, and frequently not announced. - Do not make the whole excerpt the link. A very long accessible name is its own barrier.
- Do not write “Read more about the Ceramic Mug (opens in a new window)” unless it genuinely opens in a new window — and if it does, say so, because an unannounced context change is a separate failure.
- Do not link a bare URL as its own text. Give it a name, or at minimum shorten it to the domain.
Writing good link text
| Instead of | Use |
|---|---|
| Read more | Read more about the Ceramic Mug |
| Click here | Download the size guide (PDF, 240KB) |
| More info | Delivery times and costs |
| https://example.com/returns | Our returns policy |
| Details | Ceramic Mug specifications |
Two habits carry most of the weight: front-load the distinguishing word, and never make the link text a verb with no object.
How you know it worked
The test is the one your customers use. With a screen reader running, pull up the links list — VO-U then the links menu in VoiceOver, Insert+F7 in NVDA — and read down it. If you can tell, from that list alone, where each link goes, you are done. If two entries are identical and go to different places, you are not.
A quicker sighted approximation, useful in a hurry:
// paste in the console: every link, grouped by its accessible-ish name
const byName = {};
document.querySelectorAll('a[href]').forEach((a) => {
const name = (a.getAttribute('aria-label') || a.textContent || '').trim().replace(/\s+/g, ' ');
(byName[name] ||= new Set()).add(a.getAttribute('href'));
});
Object.entries(byName)
.filter(([, hrefs]) => hrefs.size > 1)
.forEach(([name, hrefs]) => console.warn(`"${name}" → ${hrefs.size} different destinations`));
Anything that warns is a link name doing two jobs at once. That is the list to work through.
Sources: W3C WAI — Understanding SC 2.4.4: Link Purpose (In Context) · W3C WAI — Understanding SC 2.4.9: Link Purpose (Link Only) · W3C WAI — Technique ARIA8: Using aria-label for link purpose · W3C WAI — Technique H30: Providing link text that describes the purpose of a link for anchor elements
FAQ
Questions this raises
Is "read more" actually a failure? My theme generates it.
It depends on the context the link sits in, which is exactly why this is a judgement call rather than an automatic fix. WCAG 2.4.4 allows the purpose to be determined from the link together with its surrounding context — a "read more" immediately after a heading naming the product is usually acceptable. Nine of them in a grid, or one in a sidebar with no nearby heading, is not.
Why will you not just fix these automatically?
Because the correct text is a claim about your content, not about your markup. A machine can see that a link says "read more" and can guess from nearby text what it points at; it cannot know whether that guess reads correctly to your customers or matches how you talk about your products. So we draft it and you decide.
Can I keep the visible text and only change what a screen reader hears?
You can, with aria-label, and it is a legitimate technique. There is one rule: the accessible name must contain the visible text, or voice-control users end up saying a phrase that is not on the screen. "Read more about the Ceramic Mug" contains "read more" and is fine; "Ceramic Mug details" does not, and is not.
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.