Link text: why 'click here' fails and what to use
Links reading 'click here', and links wrapping an unlabeled icon, give screen reader users nothing to go on. squirrel lists both kinds.
What descriptive link text is
Screen readers can list every link on a page and read them one after another, stripped of the sentence around them. A list of twelve links that all say “read more” is unusable. WCAG success criterion 2.4.4 Link Purpose (In Context), Level A, asks that the purpose be clear from the link text, or from the sentence or list item it sits in.
An icon-only link has the same problem in a harder form: there is no text at all. It needs an aria-label, an <svg><title>, or an image with alt.
What squirrel checks
Two checks, at rule severity warning. Both walk every a[href], skipping fragment-only links whose href starts with # and links using an unsafe scheme such as javascript:.
link-text-emptyfails when a link has neither an accessible name fromaria-labelledby,aria-labelortitle, nor accessible content: text, an<img>with non-emptyalt, an<svg>carrying a<title>,aria-labeloraria-labelledby, or a[role="img"]with anaria-label. Report message:N link(s) with no accessible text. Each item is the first 50 characters of thehref, labelled with the reason:empty,svg-no-name,icon-fontorrole-img-no-name.link-text-genericwarns when the link’s accessible name, or its text, lowercased and stripped of punctuation, exactly matches one of 48 generic phrases. The list includesclick here,here,read more,learn more,more,link,download,view,subscribe,submit,next,view details,websiteanddiscover. Report message:N link(s) with generic text, listing each distinct phrase once.link-textpasses withAll links have descriptive textwhen neither problem appears on a page that has links.link-text-unconfirmedwarns withN link(s) with no accessible text may be hidden by a style (unconfirmed)for an empty link a style may hide in a way squirrel cannot settle.
Conventional labels
A link that reads “Sign in”, “Log out” or “Privacy policy” names its destination, so it is never reported as generic, even though it sits in the template of every page. The exempt set is matched against the whole link text after Unicode normalisation (NFKC, so full-width and non-breaking-space variants match, plus case folding and leading and trailing punctuation removed), and is the union across English, Spanish, French, German, Portuguese, Italian, Dutch, Japanese, Chinese, Korean and Russian, so no page language detection is needed. It covers:
- account actions: sign in, sign up, log in, log out, sign out, register, my account, and their translations
- legal and utility labels: terms, terms of service, terms and conditions, privacy, privacy policy, legal, legal notice, imprint, DMCA, cookies, cookie policy, accessibility, sitemap, contact, about, help, FAQ, careers, and their translations
Only the whole text counts: “Sign in to read more” is not exempt. click here, read more, learn more, here, more, link, this, go and continue are still reported. links/anchor-text and a11y/identical-links-same-purpose do not list these labels as generic, so they needed no change.
Generic phrases in other languages
English phrases are always checked. The page language, the primary subtag of <html lang> (so fr-CA is French), adds its own list of generic phrases: Spanish, French, German, Portuguese, Italian, Dutch, Japanese, Chinese, Korean and Russian. Examples: cliquez ici and en savoir plus (French), mehr and weiterlesen (German), haga clic aquí and leer más (Spanish), 詳しくはこちら (Japanese), 点击这里 (Chinese), 자세히 보기 (Korean), читать далее and подробнее (Russian). A page with no lang, or one that is not in that list, is checked with the English list only. Only the primary <html lang> is read: a lang on a nested element, or text in another language on a page, is not detected, so French text on an English page is not reported as generic. Ambiguous words that are also a pager or wizard button (German Weiter, Dutch verder, Russian Продолжить) are left out. Matching is on the whole link text after Unicode normalisation (NFKC, case folding, edge punctuation removed, full-width punctuation included), and CJK text is never split into words, so 詳しくはこちらの料金表 is not reported. A conventional account or legal label (Conventional labels) is never reported in any language.
Hidden links
An empty link out of the accessibility tree has nothing to announce, so it is not reported: one with the hidden attribute (except hidden="until-found"), aria-hidden="true" on itself or an ancestor, display: none on itself or an ancestor (set inline or by a <style> rule squirrel can decide), or inside a closed <dialog> or an unopened popover. It still needs a name before it is shown. A hidden link with generic text is skipped too.
squirrel reads the markup, inline styles and the page’s <style> blocks, the same way as form-labels; see form-labels for what it can and cannot decide. External stylesheets are not read. An empty link that carries a no-js class, itself or on an ancestor other than <html>, is reported as unconfirmed on a page with an external stylesheet or an @import, because a theme usually hides that fallback with a rule such as .js .no-js { display: none } once JavaScript runs.
How to fix it
<a href="/pricing">View our pricing plans</a>
<a href="/search" aria-label="Search">
<svg aria-hidden="true">...</svg>
</a>Write the destination into the link text. Where the design demands a short label, keep the short text visible and put the full phrase in aria-label, making sure the visible words appear inside it.
Matching is exact, so a download button whose visible label is precisely Download is reported even when the heading above it names the file. Lengthening the text to “Download the Q3 report” clears it.
| Rule ID | a11y/link-text |
| Category | Accessibility |
| Scope | Per-page |
| Severity | warning |
| Weight | 4/10 |
Enable / disable
Disable this rule
[rules]
disable = ["a11y/link-text"]Disable all Accessibility rules
[rules]
disable = ["a11y/*"]Enable only this rule
[rules]
enable = ["a11y/link-text"]enable replaces the default ["*"], so this is the only rule selected. Leave disable out: a rule that matches any disable pattern is skipped even when enable names it. Only an enabled setting under a rule’s own [rule_options] table overrides both lists.
Related rules
- a11y/aria-labels: accessible names for buttons and clickable
<svg>elements. It does not look at links. - a11y/identical-links-same-purpose: identical link text pointing at different pages.
- a11y/label-content-name-mismatch: an
aria-labelthat does not contain the visible words. - links/anchor-text: the same text read as a ranking signal rather than a name.
Accessibility findings ship in every audit next to the SEO, performance and agent experience rules. See Website security scan with AI for how an agent works through a report.
References
- Understanding SC 2.4.4 Link Purpose (In Context), W3C
- The a element, MDN
- H30: Providing link text that describes the purpose of a link, W3C
Check your site
Run squirrel audit https://example.com and open the Accessibility section of the report. Empty links are listed by href, generic ones by phrase. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.