accessibility

Write descriptive link text

MetricSpot fails links whose accessible name is empty, generic ("click here", "read more"), or just an icon. Screen readers announce them as "link" with no context.

What this check does

Inspects every <a href> on the page and computes the accessible name (visible text, aria-label, or alt text of a child <img>). The check fails when:

  • The name is empty (a bare <a><svg/></a> with no text or label).
  • The name is a generic phrase: “click here”, “here”, “read more”, “learn more”, “more”, “this”.
  • The same generic name points to different URLs in different places on the page — a screen-reader user listing all links hears “click here, click here, click here” with no way to tell them apart.

Why it matters

Screen-reader users navigate by listing links. In NVDA: Insert+F7. In VoiceOver: Ctrl+Option+U → Links. The user sees the link text out of context, in alphabetical order, and picks one. “Read more” gives them nothing.

  • Sighted users skim too. Eye-tracking studies (Nielsen Norman, every replication) show users scan link text first and skip surrounding prose. Descriptive links double as wayfinding cues.
  • Search ranking. Internal link anchor text is one of the few text signals Google still trusts. A “Click here” link to your pricing page wastes the slot; “MetricSpot pricing” doesn’t.
  • AI citation. ChatGPT, Perplexity, and Google AI Overviews quote anchor text when surfacing a link in a citation. “Click here” produces a citation with no preview of what the link contains.

How to fix it

Make every link’s text describe its destination, even when read out of context.

Don’t:

<p>To learn more about our pricing, <a href="/pricing/">click here</a>.</p>

Do:

<p>See our <a href="/pricing/">pricing tiers and free trial</a>.</p>

Icon-only links — use aria-label:

<a href="https://twitter.com/metricspot" aria-label="MetricSpot on X">
  <svg aria-hidden="true">…</svg>
</a>

The aria-hidden="true" on the SVG prevents the icon’s accessible content from leaking; the aria-label provides the name. Without this, screen readers announce “link” with whatever path data they can extract.

“Read more” cards (blog-post listings). The fix is to extend the anchor to cover the whole card or include the post title:

<!-- Bad: every "Read more" is identical -->
<article>
  <h3>Why your CWV are tanking</h3>
  <p>Three patterns we see…</p>
  <a href="/blog/cwv/">Read more</a>
</article>

<!-- Better: title becomes the link -->
<article>
  <h3><a href="/blog/cwv/">Why your CWV are tanking</a></h3>
  <p>Three patterns we see…</p>
</article>

<!-- Or: visually-hidden context appended -->
<a href="/blog/cwv/">Read more<span class="sr-only"> about Core Web Vitals</span></a>

Multiple identical links to different destinations. If you have several “Edit” buttons in a table, append the row context invisibly:

<a href="/users/42/edit">Edit<span class="sr-only"> Jane Patel</span></a>
<a href="/users/43/edit">Edit<span class="sr-only"> Alex Diaz</span></a>

Linters. eslint-plugin-jsx-a11y ships an anchor-has-content rule. Add a custom rule banning literal “click here” / “read more” anchor text — both are red flags 100% of the time.

Frequently asked questions

Is “Read more →” acceptable if it’s visually associated with a heading?

Sighted users associate it with the headline above; screen-reader users hear it in isolation. Either make the headline the link (best) or append <span class="sr-only"> about [post title]</span> to keep the visual design and add the context.

The icon is decorative; either hide it with aria-hidden="true" or include “(external link)” in visually-hidden text. Don’t rely on the icon alone for screen-reader users.

No — target="_blank" is a behavior, not a name. If you want screen readers to announce it, include “(opens in new tab)” as visible or sr-only text. Many users dislike unexpected new-tab behavior; if you must use it, signal it.

Sources

Last updated 2026-05-11