# "Read more" links that all say the same thing

Source: https://easeweb.dev/learn/read-more
Topics: Buttons and links > Read more links say what they lead to (WCAG 2.4.4, 2.5.3)
The fix: Give each card's link a name that says what it leads to and starts with the words it shows. Either add the article's title after "Read more" as visually hidden text, or make the title itself the link and drop "Read more", or replace "Read more" with visible words that say where it goes.
Test with: screen reader, speech input, keyboard, mouse
References: https://www.w3.org/WAI/WCAG22/Understanding/link-purpose-in-context.html https://www.w3.org/WAI/WCAG22/Techniques/failures/F63 https://www.w3.org/WAI/WCAG22/Techniques/css/C7 https://www.w3.org/WAI/WCAG22/Techniques/html/H80 https://www.w3.org/WAI/WCAG22/Understanding/label-in-name.html https://www.w3.org/WAI/WCAG22/Understanding/link-purpose-link-only.html https://ictbaseline.access-board.gov/web-baselines/14Links

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 homeware shop's journal shows its three latest articles as a row of cards: a photo, a bold title, a line from the article and a green "Read more" link with an arrow. Every card ends in the same link, so the row looks even and the eye always finds the way in at the same place. Someone using a screen reader opens its list of links to choose an article, and hears "Read more, Read more, Read more". Nothing says which one is about travel mugs, so they follow each in turn to find out.

A developer, told that the links all sound the same, gives each one an `aria-label` with its article's title. The list of links now reads well: "Caring for enamel mugs", "Why stoneware keeps tea hot", "Choosing a travel mug". But a voice control user looks at the card, says "click Read more", and nothing happens, because no link on the page is called that any more.

## Why it fails

WCAG 2.4.4 Link Purpose (In Context) lets a link's purpose come from its text together with its programmatically determined link context: text in the same sentence, paragraph, list item or table cell, the cell's table headers, or words tied to the link with ARIA. On these cards the title is a paragraph of its own and the link sits after it in a plain `div`, so in code the title isn't the link's context at all. W3C describes exactly this, a "Read More" link in the paragraph after the one it is about, as failure F63. Read alone, as a screen reader reads it on Tab and in its list of links, "Read more" says nothing about where it goes.

How the card is built changes the answer. Had each card been a list item, the title would be in the same list item as the link (technique H77), and the link would most likely pass 2.4.4. A real heading before the link is weaker: W3C lists reading a link with the heading before it only as an advisory technique (H80), and neither its definition of link context nor the US Section 508 baseline test names headings, though some auditors accept one. Either way, the list of links still holds three identical entries, which fails the Level AAA criterion 2.4.9 Link Purpose (Link Only).

The quick fix trades one failure for another. An `aria-label` replaces the link's text as its name, so each link now says where it goes and passes 2.4.4, but the words on screen, "Read more", are no longer in its name. WCAG 2.5.3 Label in Name (Level A) asks that the name of a control with visible words contains them, so that people can operate it by saying what they see. The [buttons and links named differently from their text](/learn/label-in-name) guide covers that failure on its own.

## Who is affected

The pattern is everywhere cards repeat: blog and news listings, product teasers, search results and help-centre tiles. "Learn more", "Shop now", "Details" and "Click here" fail in the same way.

## What automation and AI miss

axe's `link-name` rule passes every "Read more", because each has text. Whether the words around a link name its destination takes judgement, so the audit records every link with a generic name, such as "Read more", "Learn more" or 詳しくはこちら, as a lead, with the paragraph, list item, table cell, description and heading a screen reader can reach from it, and how many links share the name but go elsewhere. ANDI adds its own warning when links share a name and not an address. A person, or the audit's AI review, then decides whether the context says where each one goes. For the second Before, axe's experimental rule `label-content-name-mismatch`, which the audit runs and reports as a lead, compares the aria-label with the visible text and flags it. Asked to fix "links with the same name", an AI coding tool can write the second Before.

## Before: the same Read more on every card

```html
<div class="cards">
  <div class="card">
    <img src="/images/journal/enamel-mug.jpg" alt="">
    <p class="card-title">Caring for enamel mugs</p>
    <p>A soft sponge and no scouring powder keep the enamel bright.</p>
    <a href="/journal/enamel-care" class="card-more">Read more</a>
  </div>
  …
</div>
```

```css
.card-title {
  font-size: 1.125rem;
  font-weight: 700;
}
```

The title looks like a heading but is a paragraph, and the link is in neither paragraph, so nothing in code ties "Read more" to the article it opens. It fails 2.4.4, and every card's link is the same entry in the list of links.

## Before: an aria-label that leaves out the words on screen

```html
<div class="cards">
  <div class="card">
    <img src="/images/journal/enamel-mug.jpg" alt="">
    <p class="card-title">Caring for enamel mugs</p>
    <p>A soft sponge and no scouring powder keep the enamel bright.</p>
    <a href="/journal/enamel-care" class="card-more" aria-label="Caring for enamel mugs">Read more</a>
  </div>
  …
</div>
```

```css
.card-title {
  font-size: 1.125rem;
  font-weight: 700;
}
```

The `aria-label` becomes the name, so the link now says where it goes and passes 2.4.4. But its name doesn't contain "Read more", the words it shows, so it fails 2.5.3, and "click Read more" finds nothing.

## After: hidden words after Read more

```html
<div class="cards">
  <div class="card">
    <img src="/images/journal/enamel-mug.jpg" alt="">
    <p class="card-title">Caring for enamel mugs</p>
    <p>A soft sponge and no scouring powder keep the enamel bright.</p>
    <a href="/journal/enamel-care" class="card-more">
      Read more<span class="visually-hidden"> about caring for enamel mugs</span>
    </a>
  </div>
  …
</div>
```

```css
.card-title {
  font-size: 1.125rem;
  font-weight: 700;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
```

The smallest change, and the design stays exactly as drawn. The link's name is now "Read more about caring for enamel mugs": it says where it goes in the list of links, which passes 2.4.4 and 2.4.9 (technique C7), and it starts with the words on screen, which passes 2.5.3. "Click Read more" matches all three cards, and voice control such as Dragon numbers them to choose from, as it does for any repeated label. `aria-labelledby="more-enamel title-enamel"`, listing the link's own `id` and then the title's, gives a similar name without the span, "Read more Caring for enamel mugs", at the cost of two `id`s per card.

## After: the title as the link

```html
<div class="cards">
  <div class="card">
    <img src="/images/journal/enamel-mug.jpg" alt="">
    <h3 class="card-title">
      <a href="/journal/enamel-care" class="card-link">Caring for enamel mugs</a>
    </h3>
    <p>A soft sponge and no scouring powder keep the enamel bright.</p>
  </div>
  …
</div>
```

```css
.card-title {
  font-size: 1.125rem;
  font-weight: 700;
}

/* The title's link covers the whole card. */
.card {
  position: relative;
}

.card-link::after {
  content: "";
  position: absolute;
  inset: 0;
}

.card-link:focus-visible {
  outline: none;
}

.card-link:focus-visible::after {
  outline: 3px solid var(--focus-ring);
  outline-offset: -3px;
}
```

"Read more" goes, and the title people already read becomes the one link on the card: its name is the title, so the list of links reads "Caring for enamel mugs", and voice control users say the title they see. It passes 2.4.4, 2.4.9 and 2.5.3, and the title is now a heading as well, so screen reader users can move from card to card by heading. The pseudo-element stretches the link's click area over the whole card; leave it out if only the title should be clickable. Any other link or button in the card, such as a Save button or a tag, needs `position: relative` and a `z-index` above the pseudo-element, or it can't be clicked.

## After: words that say where it goes

```html
<div class="cards">
  <div class="card">
    <img src="/images/journal/enamel-mug.jpg" alt="">
    <p class="card-title">Caring for enamel mugs</p>
    <p>A soft sponge and no scouring powder keep the enamel bright.</p>
    <a href="/journal/enamel-care" class="card-more">Read the care guide</a>
  </div>
  …
</div>
```

```css
.card-title {
  font-size: 1.125rem;
  font-weight: 700;
}
```

Each card's link gets its own visible words: "Read the care guide", "Read about stoneware", "Compare travel mugs". The name is the text, so it passes 2.4.4, 2.4.9 and 2.5.3 with no hidden text and nothing to keep in step, and the words help everyone who skims, not only screen reader users. The look changes: the links are no longer one repeated label, they differ in length, so the row is less even, and every card needs someone to write its link.

## Which option to use

All three pass 2.4.4 and 2.5.3, and all three make the links clear on their own, as 2.4.9 asks. Hidden words change nothing on screen, so they suit a design that must keep "Read more", or a theme you can only add to. Saying "click Read more" then matches every card, and Apple's Voice Control has been reported to need the whole name, so on a Mac or an iPhone the person may have to use numbers. The title as the link is the cleanest markup, one link per card named by words everyone sees, and it suits cards whose title says enough; the stretched click area brings its own care with other controls in the card. Visible words suit a design that wants a call to action on each card, and need a writer for each one. None is right for every site, so choose by what the design must keep.

## Implementation decisions

**Hidden text, not aria-label.** If you do want an `aria-label`, start it with the visible words: "Read more about caring for enamel mugs". Hidden text is still the better choice: it is translated with the page, while some browser translation tools leave an `aria-label` in the original language, which brings the mismatch back. Use a clip-based `.visually-hidden` class, never `display: none`, which removes the words from the name too.

**aria-describedby doesn't fix the list.** Pointing `aria-describedby` at the title adds it as a description, read after "Read more, link". W3C's failure test and the Section 508 test both count a description, so the link most likely passes 2.4.4, but a screen reader's list of links shows names only, and still reads "Read more" three times.

**A hidden Read more beside a heading link.** A common pattern keeps the visible "Read more" but takes it out of everything: `aria-hidden="true"` so screen readers skip it, `tabindex="-1"` so Tab skips it, with the title as the real link. Screen readers then hear one link per card, but it is not a clean fix. A voice control user who says "click Read more", the words they can see, gets nothing, because speech software that works from the accessibility tree can't find a hidden link. A sighted keyboard user sees a link that Tab never reaches. Hide something from everyone or no one; if the words should stay, give them to the link, as the hidden-words fix does.

**A whole card in one link.** Wrapping the card in one `<a>` is valid HTML, but the link's name becomes everything inside the card, so the screen reader reads a paragraph, and the card can't hold any other link or button. A link on the title, stretched, avoids both.

**A card title is usually a heading.** A title drawn bigger and bolder than the text around it reads as a heading, and screen reader users navigate by headings. Marking it as one, at the level that fits the page, as in the title-as-link fix, helps them whichever fix you choose. See [headings chosen by size, not by structure](/learn/heading-structure).

**Every generic label is the same failure.** "Learn more", "Shop now", "Details", "Click here" and "View" repeated over cards fail in the same way and take the same fixes. In a CMS or a design system, give the card component a field for the link's words, or for the hidden words after "Read more", and fill it from the article's title when an editor leaves it empty, so a new card can't publish with a bare "Read more".

**A Read more that opens text in place is a button.** When "Read more" shows the rest of a review or description on the same page instead of going to another one, it is a `button`, never a link to `href="#"`, with `aria-expanded` saying whether the text is open. Name it after its item in the same way when several sit on one page. [Show and hide buttons that don't say whether they are open](/learn/show-hide-buttons) covers the button itself.

## Verify the fix

1. Open the screen reader's list of links (the rotor in VoiceOver, Insert + F7 in NVDA and JAWS). Each card's link must say which article it opens and be distinct from the others.
2. Inspect each card's link in the browser's accessibility tree, and check that its name contains the words it shows and starts with them.
3. With voice control, say "click" and the words shown on a card's link, and check it follows that link, or that numbers appear to choose between the links that share the words and choosing one follows it.
4. Click across each card where the design shows it as clickable, including at 200% zoom and on a touch screen, and check that any other control inside the card can still be pressed.

Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the shared card component or its template changes.

## Limits

This is a starting pattern, not a guarantee for every application. Paths and class names in the snippets are placeholders. Speech software differs in how it matches what is said, so test with the voice control your users have. Editors can still publish a card with a vague link later, so keep link text in your publishing review. A banner made clickable with an empty link is covered in [banner and image links with no name](/learn/empty-links). Test the complete journey with your target assistive technology.

---

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