A link is an element that says "this text points at that thing", and almost everything interesting about links comes from the choices involved: what you point at, what you write as the text, and whether the link replaces the page or opens beside it.
What does a link look like?
The href attribute holds the destination, and the text between the tags is what the reader sees and clicks.
<a href="/tutorials/python">the Python tutorial</a>
<a href="https://example.com/docs" rel="external">the documentation</a>
<a href="#installation">jump to installation</a>
<a href="mailto:[email protected]">email us</a>
Four kinds of destination appear here, and they are worth telling apart. A root-relative path starts with a slash and always resolves from the domain root, so it keeps working wherever the page is nested. A relative path resolves from the current page's folder, which is what you use for links inside the same project. An absolute URL points at another site. And a fragment — the part after # — points to a place inside the current page.
How do anchors work?
An anchor is a link to an element with a given id. The target does not have to be a heading, and the id can be on any element.
<h2 id="installation">Installation</h2>
<p>...</p>
<a href="#installation">skip to installation</a>
Two behaviours are worth knowing. The browser scrolls to the target and puts it at the top of the viewport, so if a fixed header covers the heading, the anchor appears to do nothing — the standard fix is a scroll-margin-top on the target. And an anchor to an id that does not exist does nothing at all, silently, which is why a broken in-page link is hard to notice until someone reports it.
/* a fixed header no longer hides the heading an anchor points to */
:target {
scroll-margin-top: 5rem;
}
What should the link text say?
"Click here" tells the reader nothing until they click, and it is useless out of context — in a screen reader's list of links, or when the link is copied into a message. The link text should say where the link goes.
<!-- weak: the destination is invisible until you click -->
<a href="/tutorials/python">click here</a>
<a href="/docs/api">read more</a>
<!-- strong: the text stands on its own -->
<a href="/tutorials/python">the Python tutorial</a>
<a href="/docs/api">the API reference</a>
Screen readers often offer a way to list every link on a page. In that list, "click here" appears once for every link on the site, and a list of identical entries tells the reader nothing. Descriptive link text is one of the cheapest accessibility wins available.
Should the link open in a new tab?
Probably not, and the reason is behavioural rather than aesthetic. A link that replaces the page gives the reader an unambiguous back button, because their history moves forward. A link that opens in a new tab leaves the current page in place, so the reader ends up with two tabs and has to work out which one they were reading. It is also easy to lose the reference to the original page.
<!-- a link to a PDF or a tool may justify a new tab -->
<a href="/reports/annual-summary.pdf" target="_blank" rel="noopener noreferrer">
the annual summary (PDF)
</a>
When you do open a new tab, rel="noopener noreferrer" matters: without it, the new page can reach back into the original one through window.opener, which is a security problem and a privacy problem at once. Add the link text as a hint too, so the reader knows to expect something new.
How do email and telephone links work?
They use their own schemes, and the difference is what happens after the click.
<a href="mailto:[email protected]?subject=Question%20about%20the%20tutorial">
email the team
</a>
<a href="tel:+441234567890">call 01234 567890</a>
An email link opens the visitor's mail client with the address filled in; the subject parameter pre-fills the subject line, and it must be URL-encoded, which is why the space became %20. A telephone link opens the dialler on a phone, and is ignored on a desktop — so never make it the only way to reach someone.
How do you help keyboard users skip repeated content?
Every page has the same navigation before the main content, and a keyboard user has to tab through it on every page. A skip link solves this: it is the first focusable element on the page, invisible until it receives focus, and it jumps straight to the main content.
<a class="skip-link" href="#main">Skip to content</a>
<nav>...</nav>
<main id="main">...</main>
.skip-link {
position: absolute;
left: -9999px;
}
.skip-link:focus {
left: 1rem;
top: 1rem;
padding: 0.5rem 1rem;
background: #fff;
z-index: 10;
}
It is one of the highest-value accessibility features on a site, and it is invisible to everyone else. The rule is that the link must be the first thing in the document, so it is the first stop for the tab key.
What is the difference between an absolute and a relative link?
The target of a link can be written in several ways, and the difference matters the moment your site moves to a different folder or a different domain.
A root-relative link starts with a slash: /about/team. The browser resolves it against the root of the site, so it works from any page in the project. A document-relative link has no leading slash: ../about/team. The browser resolves it against the folder of the current page, so it only works if the folder structure never changes. An absolute link includes the whole address, including the scheme and the domain: https://example.com/docs.
<!-- root relative: works from every page in the site -->
<a href="/pricing">Pricing</a>
<!-- document relative: depends on the current folder -->
<a href="../pricing.html">Pricing</a>
<!-- absolute: includes the scheme and the host -->
<a href="https://tutorsbot.com/pricing">Pricing</a>Root-relative is the usual choice for a site with a fixed set of top-level pages, because it survives a page being moved into a different folder. Absolute is the right choice for a link that leaves your site entirely, and for a link you want to keep working if the site is served from a different address.
One more thing to know: the base element in the head can change what every relative link on the page is resolved against. It is occasionally useful for a generated page, and occasionally the reason a link that looks right in the source goes somewhere unexpected - so check for it when a relative path misbehaves.
Quick reference
- Root-relative paths (
/page) keep working at any nesting depth; relative paths depend on the current folder. - An anchor points at an
id, and does nothing if that id is missing. - Link text should describe the destination, not say "click here".
- Prefer replacing the page; use
rel="noopener noreferrer"if you open a new tab. mailto:andtel:open external apps, so they need a visible alternative.
Try it yourself: build a page with three headings that have ids, a table of contents of anchor links at the top, and a link out to another site. Then scroll to an anchor while a fixed header is present and add the scroll-margin-top fix so the heading is not hidden.