The link said that a link's text is its name. This page is about the person on the other side of the link: someone scanning quickly, listening to a screen reader, using only a keyboard, or tapping a small screen with an unsteady hand. The accessibility and usability pages explain why the course treats these as qualities of every page. The success criteria quoted here are from WCAG 2.2, the current version of the Web Content Accessibility Guidelines; level A and AA criteria are the ones most laws and policies require.
1. Link text that makes sense on its own
People rarely read a web page from start to finish. Sighted readers scan it, and links are among the things their eyes stop on, because links are colored and underlined and promise somewhere to go. Screen reader users scan too, with different tools. Every major screen reader can list the links on a page: VoiceOver on macOS has its rotor (Control+Option+U), NVDA its Elements List (Insert+F7), and JAWS a links list (Insert+F7). Many users also jump from link to link with the Tab key and hear each one announced by itself. Voice control users say a link's name to follow it. In each case, the link's name is heard or seen without the sentence around it.
Why "click here" fails
"Click here" fails three times. It says nothing about the destination, so five of them make five identical entries in a links list. It names an action many people do not perform: they press Enter, tap or speak. And it leaves the useful words, the ones a scanning reader looks for, outside the link. "Read more", "learn more", "more", "here", "this" and "link" fail the same way.
The fix is almost always to move the link onto the words that name the destination, and delete the instruction:
The words a link names should also say what kind of thing is at the other end when it is not an ordinary page: "the syllabus (PDF, 240 KB)", "email the instructor", "call the help desk". Section 6 returns to this.
Bare URLs
An address used as its own link text, such as https://cse134.site/interaction/linking/trust/, is precise but hard to use. A screen reader reads it out, often a character or a word fragment at a time, and a long address with a query string takes many seconds to hear. Use the name of the page instead. A visible address is right when the address itself is the content: in print, in a citation, or for a short address such as cse134.site that a reader may want to type elsewhere.
What WCAG asks for
Two success criteria cover link text. 2.4.4 Link Purpose (In Context), level A, asks that the purpose of each link can be determined from its text alone or from its text together with its programmatically determined link context: the sentence, paragraph or list item that contains the link, or a table cell and its headers. "Read more" at the end of a paragraph that describes the article can pass 2.4.4, because a screen reader user can ask for the surrounding paragraph. 2.4.9 Link Purpose (Link Only), level AAA, asks that the link text alone be enough. The first is the legal minimum; the second is the one to aim for, because a links list never shows the paragraph, and a reader using one would have to leave the list to find it.
Same text, different places
Two links with the same name should go to the same place. A news page with ten "Read more" links gives a links list ten identical entries leading to ten different articles. A table of products with "Details" in every row does the same. The reverse is fine: two links with different names can go to the same place, though section 7 shows why two links side by side to the same address are worth combining.
When the visible text has to stay short
Sometimes a design really needs short visible text, such as a row of cards each ending in "Read more", or a table with "Edit" in every row. There are three ways to give each link a full name while keeping the short text on screen.
- Visually hidden text inside the link. Text that is hidden from sight but kept in the accessibility tree becomes part of the link's name: "Read more about skip links". This is the most robust approach, because it is plain text: it is translated with the page, it survives copying, and it works in every screen reader. Never use
display: noneorhiddenfor it; those remove the text from the accessibility tree too. aria-labelledby, pointing at the link itself and the card's heading, so the name is built from text already on the page: "Read more, Skip links".aria-label, which replaces the link's text for assistive technology. It works, but the name is invisible to everyone else, often missed by page translation tools, and easily left out of date when the visible text changes. WCAG 2.5.3 Label in Name adds a constraint: the name must contain the visible words, so a voice user who says "click read more" reaches the link.aria-label="Read more about skip links"passes;aria-label="Skip links article"does not.
The visually hidden pattern is a few lines of CSS that most projects already have, often as a visually-hidden or sr-only utility:
The last example above uses aria-describedby, which is different from the other three. A description is announced after the name and is not part of it: it is not in a links list, and voice users do not say it. That makes it the right place for information about a link that is not its destination, such as a file's size, "opens in a new tab", or "external site". The destination itself belongs in the name.
The demo is a short course announcement with a mix of good and bad links. Its button lists every link by its computed name, alone, and flags the problems.
2. Links that look like links
A link nobody recognizes is a link nobody follows. Of the web's convention, blue underlined text, the underline is the part that matters: it works in grayscale, for people who cannot distinguish colors, in forced-colors modes, in sunlight and in print.
Color is not enough
WCAG 1.4.1 Use of Color, level A, says color must not be the only visual means of conveying information, and a link's linkness is information. A link inside a paragraph that differs from the text around it only by color fails, unless the difference is strong enough to see without color vision. W3C technique G183 describes that case: if inline links are distinguished from the surrounding text only by color, the link color needs a contrast ratio of at least 3:1 against the surrounding text, in addition to the usual 4.5:1 of both against the background. Few color pairs meet all three ratios at once; a dark blue link in black text, for example, rarely does. The technique also mentions, as an optional extra, an underline or bold weight. An underline is simpler, and passes without arithmetic.
Why links default to blue, and visited links to purple, is a small piece of history told in How color works; Why color matters makes the general case for never letting color carry meaning alone.
The rule is about links in running text, where they sit next to words that look the same. Links in a navigation bar, a list of links, or a button-shaped call to action are identified by their position and shape, and can do without an underline.
Underlines that read well
Designers removed underlines for years because the browser's default line was heavy and cut through descenders, the tails of letters such as g and y. CSS has fixed both. text-underline-offset moves the line away from the text, text-decoration-thickness sets its weight, and text-decoration-skip-ink, on by default, breaks the line around descenders. The first two have worked in every major browser since 2021 (Chrome 87 and 89, Firefox 70, Safari 12.1). A hover style that thickens the underline gives a pointer user feedback without changing the layout.
Relative units keep the underline in proportion when someone enlarges the text.
Visited links, and why they are restricted
A different color for visited links is one of the oldest usability features on the web: it tells a person which pages they have already read, which matters most in search results and long lists. Many modern designs drop it. Keep it on pages where people work through a list.
What a page can do with :visited is deliberately limited, because the selector once leaked people's browsing history. The attack was simple. A page made a long list of links to sites it was curious about, such as banks, clinics, political groups and competitors, styled visited links differently, then asked the browser which ones had the visited style, with getComputedStyle() or by checking which links had a different size. In a few milliseconds and without any permission, the page knew which of those sites the person had been to. Researchers showed it at scale in the late 2000s, and some sites were found using it.
Around 2010 the browsers closed the direct routes, and those rules still hold:
- Only colors can change. A
:visitedrule may setcolor,background-color, theborder-colorproperties,outline-color,column-rule-color,text-decoration-colorandtext-emphasis-color, and SVG'sfillandstroke. Anything else, such as font weight, size, an underline style, a background image or generated content, is ignored, because a change of size or layout could be measured. - The alpha channel is ignored. A visited link gets the transparency of the unvisited style, so a page cannot make visited links invisible and detect it.
- Scripts are told a lie.
getComputedStyle()returns the unvisited style for every link, and selector APIs such asquerySelectorAll(':visited')match nothing.
Those rules stopped the easy version, but researchers kept finding side channels, such as timing how long the browser took to paint a visited link. The deeper fix is to stop one site from seeing history made on another. From Chrome 136 (2025), Chrome partitions its visited-link history: a link is shown as visited only if it was followed from the same top-level site and the same frame origin. A link to a site's own pages is an exception, since the site already knows where its visitors have been on it. Firefox and Safari keep the style restrictions but, at the time of writing, still keep one global visited history.
For a page author the practical result is small: style :visited with a color, and expect nothing else to work. The broader lesson, which the privacy page returns to, is that a purely visual feature can reveal something about a person.
3. Focus people can see
Keyboard users know where they are on a page only from the focus indicator, the ring the browser draws around the focused link. WCAG 2.4.7 Focus Visible, level AA, requires that some indicator is visible. Every browser draws one by default, so the most common failure is a stylesheet that removes it, often with outline: none on every link because the ring appeared after a mouse click and someone disliked it.
:focus-visible
That complaint has a proper answer. :focus-visible matches an element when the browser's heuristics say a focus indicator should be shown: always after keyboard navigation, but not usually when a link or button was clicked with a mouse. It has been supported in every major browser since early 2022 (Chrome 86, Firefox 85, Safari 15.4), and the browsers' own default rings already use it. A custom style should be at least as visible as the default, and should use outline, not only box-shadow: in Windows' forced-colors mode, which replaces the page's colors with the person's own, box shadows are removed while outlines are kept and recolored.
Focus that is not hidden
WCAG 2.2 added 2.4.11 Focus Not Obscured (Minimum), level AA: when something receives keyboard focus, it must not be entirely hidden by content the author added. (The AAA version, 2.4.12, asks that no part of it be hidden.) The usual offenders are sticky headers and footers, cookie banners and chat widgets. The browser scrolls each focused link into view, and it lands underneath a fixed header; fragment targets do the same.
CSS fixes both with scroll padding on the scrolling container, which tells the browser to leave room at the edge when it scrolls something into view, for focus and for fragments alike:
For banners the fix is design: one that covers focused content should be dismissible from the keyboard. Forms for people covers the rest of keyboard use, including why positive tabindex values, accesskey and stray autofocus hurt more than they help; the same advice applies to links.
4. Skip links
Most sites put the same header and navigation at the top of every page. A sighted mouse user's eyes jump straight past it. A keyboard user has to Tab through every link in it, on every page, before reaching the content. On a site with a twenty-link menu, that is twenty key presses per page. WCAG 2.4.1 Bypass Blocks, level A, asks for a way to skip blocks of content that repeat across pages.
A skip link is the simplest way. It is the first focusable thing in the page, it says "Skip to main content", and it is a fragment link to the <main> element. Following it scrolls to the main content and, in current browsers, moves the sequential focus navigation starting point there, so the next Tab goes to the first link in the content rather than back into the menu. The target does not need a tabindex for that to work, though some authors add tabindex="-1" to <main> so that focus itself moves and every screen reader announces the new position.
The link can be visible all the time, but most sites hide it until it has focus. Hide it by moving it off screen, never with display: none or visibility: hidden, which also remove it from the tab order, and show it at the top of the page when it has focus.
Screen reader users have other ways past the menu. Landmarks, the header, nav, main and footer elements, can be listed and jumped to, and headings can be navigated one by one. The skip link is still worth having, because the people who most need it are sighted keyboard users, who have no landmark menu.
5. Big enough to hit
A link is a target, and small targets are hard to hit for people with tremors or limited dexterity, for anyone on a phone held in one hand, and for everyone on a bus. WCAG 2.5.8 Target Size (Minimum), level AA, new in WCAG 2.2, asks for pointer targets of at least 24 by 24 CSS pixels. A smaller target passes if it has enough space around it: a circle 24 pixels across, centered on it, must not touch another target or another small target's circle. The AAA criterion, 2.5.5, asks for 44 by 44, which is close to what phone makers' own guidelines recommend.
The criterion has exceptions, and the one that matters most for links is the inline exception: a link inside a sentence, whose height is set by the line of text it sits in, is exempt. Nobody expects a paragraph's line height to be doubled for its links. Others cover a large-enough equivalent elsewhere on the page, controls sized by the browser, and essential presentations.
The links that most often fail are the ones outside sentences: icon links in a toolbar or a footer, social media icons, pagination numbers, "×" close links, and lists of links stacked with no space between them. Padding is the fix, because padding is part of the link's target while margin is not. In rem, 24 CSS pixels is 1.5rem at the default text size, and the target grows with the text if someone enlarges it, which is what they would want.
Forms for people applies the same criterion to fields and buttons, where labels do much of the work.
6. Say what will happen
An ordinary link has a contract with the person following it: it loads a page, in this tab, and the back button undoes it. A link that breaks the contract should say so before it is followed. The slides this course grew from put it as a list of questions a link should answer: where does it go, inside the site or outside it; will it cost anything; is it secure; what will it do. The common cases are new tabs, files and downloads, and leaving the site.
New tabs and windows
A link that opens a new tab takes a choice away from the person, who could have opened it in a new tab themselves, and breaks the back button, which does nothing in the new tab. For screen reader users and people using magnification the switch can go unnoticed: they follow a link, the page changes, and "back" no longer works. WCAG's level AAA criterion 3.2.5 Change on Request counts opening a new window without warning as a failure (failure F22), and technique G201 describes the fix: give advance warning, both visible and announced. Level AA does not require it, but the warning costs a few words and the surprise costs more.
Most links should open in the same tab. A new tab is reasonable when leaving the current page would lose something: help text opened from the middle of a long form, or a reference opened from a timed exam. What a click does covers the target attribute itself and the opener. When a link does open a new tab, say so in its text, or in an icon that has text for assistive technology:
In the second version the icon is hidden from assistive technology and the hidden text carries the warning, so sighted readers see the icon and screen reader users hear the words. W3C's own example of G201 does the same with aria-describedby instead, which can point at an element with the hidden attribute and still be computed from it, so one hidden note, "opens in a new tab", can serve every such link on the page. An icon on its own, with no text, warns only the people who already know what that icon means.
Files and downloads
A link to a PDF, a spreadsheet, a ZIP archive or a video does something different from a link to a page. It may download instead of opening, it may open in another application, and it may be large: a 40 MB file on a metered phone plan is a real cost. The link text should give the format and the size:
The format can go in the name, as here, or in a description; the size is a description, since it is information about the link rather than where it goes. A PDF linked as a page should usually be a page: an HTML version is easier to read on a phone, to search, to translate and to use with assistive technology, and a PDF link is best offered alongside it, for printing. What a click does explains the download attribute and when it applies.
Leaving the site
Most links to other sites need no warning; that is what the web is for. A warning helps when leaving changes what the person can trust, such as a bank's page linking to a third-party offer. Then a few words, such as "(external site)", tell the person they are leaving a site they trust for one they may not. Links you can trust covers the other side of this: how a person can tell where a link really goes.
Indicators from CSS, and their limits
Attribute selectors can add an indicator to every link of a kind without touching the markup: [href$=".pdf"] matches addresses that end in .pdf, [href^="mailto:"] addresses that start with mailto:, and [target="_blank"] links that open a new tab. The content property can give generated text an alternative for assistive technology after a slash, which every major browser supports (Chrome 77, Safari 17.4, Firefox 128). A link can also declare its destination's format with the type attribute, type="application/pdf", which is only a hint written by the author, but is a more reliable thing to select on than the end of an address.
This is a useful safety net and a poor substitute for words in the link. Its limits:
- It only sees the markup.
[href$=".pdf"]missesreport.pdf?v=2, misses/files/reportwhen the server sends a PDF from an address without an extension, and missesREPORT.PDFunless the selector has theiflag. It cannot know a file's size at all. - Generated text is not content. It is not part of the document, so it is not copied with a selection, it disappears if the stylesheet fails to load, and screen reader support for generated content, while good today, has been uneven; older browsers ignore the text after the slash and read the arrow's Unicode name, or nothing.
Use the CSS indicator as a visual reinforcement. Put the words in the HTML.
7. Image links
When an image is the only content of a link, the image's alt text is the link's name. That changes what the alt text should say: not what the picture shows, but where the link goes. A logo that links to the home page is alt="Trail Notes, home", not alt="logo" and not a description of a mountain in green. An arrow linking to the next page is alt="Next page". An image link with alt="", or with no alt at all, is a link with no name: a screen reader announces it as "link", or reads its address, and a links list shows a blank or a file name. Images for people calls these functional images and puts them in its decision tree for alt text; SVG covers icons drawn in SVG, which need a <title> or an aria-label on the link in the same situation.
In the last example the image's alt is empty because the link already has a name. Giving it alt="Ridge trail" would make the link's name "Ridge trail Ridge trail"; giving it a description of the photograph would bury the destination in the middle of a sentence nobody asked for.
Two links to the same place
The card is the web's most common layout: an image, a heading, a short summary, often a "Read more". Built naively, the image is a link, the heading is a link, and the "Read more" is a link, all to the same address. A keyboard user tabs three times per card; a screen reader announces the destination three times; a links list shows each article in three versions, one of them probably an image's description. WCAG technique H2 describes the simplest repair, for an image next to text that link to the same place: put both in one link, and give the image an empty alt.
When a card has more than a heading and image, wrapping all of it in one link makes a long, rambling name, the summary included. A cleaner pattern links only the heading and stretches that link's target over the whole card with a pseudo-element, so a click anywhere on the card follows it, while the link's name stays the heading. The link covers block links in general.
The cost: the pseudo-element covers the card's text, so selecting the summary is awkward, and any other link in the card must be lifted above it with a z-index.
8. Image maps
An image map makes regions of a single image into separate links: the rooms of a floor plan, the states on a map, the parts of a diagram. HTML has had them since the mid-1990s; HTML 3.2 standardized the form still used today, the client-side image map, where the page itself describes the regions. (Before that, server-side image maps, with the ismap attribute, sent the click's coordinates to the server, which decided where they led. They gave keyboard and screen reader users nothing to work with and are only history now.)
A client-side image map has three parts. The image names a map with usemap, written like a fragment. The <map> element has a matching name. Inside it, each <area> is a link with a shape, coordinates and an href:
The shapes are rect (left, top, right, bottom), circle (center and radius), poly (a list of points) and default (the rest of the image). Each area with an href must have an alt, and that alt is the link's name, so it names the destination, exactly as for any image link. The image keeps its own alt, describing the image as a whole. Done this way, image maps are more accessible than they look: each area is a real link, in the tab order, in a screen reader's links list, with a focus ring that most browsers draw around the area's shape.
Why they are rare now
The coordinates are in CSS pixels of the image as drawn, and they do not scale. Make the image responsive, with max-inline-size: 100%, and on a narrow screen the picture shrinks while its areas stay where they were, over the wrong rooms or off the edge entirely. Scripts can recompute the coordinates on resize, at the cost of a dependency. Areas also show no hover or visited state, so a person cannot see where the links are until they find them.
Inline SVG solves both. An <a> element inside an SVG wraps any shape, the shapes scale with the drawing, the links take their names from text in the drawing, and CSS can style hover and focus on each shape. For a new design, SVG is usually the better choice. A classic image map still makes sense when the image is a photograph or a scan that cannot be redrawn, and when its size can be fixed. In either form, the regions are links people may not find, so offer the same links as ordinary text nearby.
9. Navigating within a page
Long pages need ways to move around inside them. The fragment links that Where a link goes introduced are the tool: a table of contents at the top, a link on each heading so people can share a section, and a way back to the top.
Tables of contents
A table of contents is a list of fragment links in a <nav> with its own label, so it appears as a separate landmark from the site's main navigation. Its link text should match the headings it points to, so that the heading a person lands on confirms they are in the right place.
This page has one, built for it by the site from its headings, and each heading carries a link to itself. Following a fragment link adds a history entry, so the back button returns to the table of contents, a detail What a click does explains. The scroll-padding from section 3 keeps each heading from landing under a sticky header. If the page scrolls smoothly, with scroll-behavior: smooth, put that inside a prefers-reduced-motion: no-preference query, because long animated scrolls make some people ill.
Back to top
A "back to top" link saves a long scroll on a long page, especially on a phone, where the browser's own shortcut, tapping the status bar, is not something everyone knows. HTML has a special case for it: a fragment of #top scrolls to the top of the document even when no element has that id. Give it a real target anyway, such as <header id="top"> or the skip link's main, so that the browser has an element to move the keyboard focus starting point to, and a keyboard user's next Tab starts from the top as well.
Put the link where people finish reading: at the end of each long section, or once at the bottom. A floating button that follows the scroll covers content and is one more thing for 2.4.11 to catch. A page that needs many "back to top" links may need a table of contents more, or splitting into several pages.
10. Summary
| Concept | Key points |
|---|---|
| Link text | Heard and seen out of context: links lists, Tab, voice control. Link the words that name the destination; no "click here", "read more" or bare URLs. WCAG 2.4.4 (A) allows the surrounding sentence; 2.4.9 (AAA) asks for the text alone. Same name, same destination. |
| Short visible text | Visually hidden text in the link (most robust), aria-labelledby, or aria-label containing the visible words (2.5.3). aria-describedby for extra information, such as size or "opens in a new tab", that is not the destination. |
| Looking like a link | Underline links in prose (1.4.1); color alone needs 3:1 against the text around it (G183). text-decoration-thickness, text-underline-offset, a hover change. |
:visited | Colors only, alpha ignored, scripts see the unvisited style: closes history sniffing. Chrome 136 partitions visited history by link, top-level site and frame origin; Firefox and Safari do not yet. |
| Focus | Never remove the ring without a replacement (2.4.7). :focus-visible for keyboard focus; use outline, which survives forced colors. Sticky headers must not hide focus (2.4.11): scroll-padding-block-start. |
| Skip links | First focusable element, "Skip to main content", href="#main" to <main id="main">. Hide off screen, never display: none; show on :focus. Bypass Blocks (2.4.1); most useful to sighted keyboard users. |
| Target size | 24 by 24 CSS pixels (2.5.8, AA), or spacing so 24-pixel circles do not touch; links in sentences exempt. Pad nav, icon and footer links; 1.5rem at default size. |
| Warnings | Say so before a new tab (G201; F22 under 3.2.5), a file (format and size), or leaving a trusted site. Text in the HTML; CSS indicators with content: "…" / "alt" only as reinforcement. |
| Image links | Alt names the destination ("Trail Notes, home"). Image plus text in one link: alt="". Cards: one link, on the heading, stretched with ::after. |
| Image maps | usemap, map name, area with shape, coords, href and required alt. Coordinates do not scale; prefer inline SVG with a; offer the links as text too. |
| In-page navigation | Table of contents in a labeled nav, text matching headings. #top works without a target, but give it one for focus. Smooth scrolling only under prefers-reduced-motion: no-preference. |
