The element the web is built on
<a href="…"> turns text into a destination. The text inside it is what a reader clicks and what a screen reader announces, so it has to make sense on its own: many people navigate by listening to a list of every link on the page. "Read the CSS guide" works there; "click here" does not.
<a href="/learn/css/">Another page on this site</a>
<a href="https://developer.mozilla.org/">Another site</a>
<a href="#forms">A heading further down this page</a>
<a href="mailto:hello@example.com?subject=Doors">Start an email</a>
<a href="tel:+994501234567">Call, on a phone</a>
What a URL is made of
- scheme —
https:almost always.mailto:,tel:and#are also schemes of a sort. - host — the domain. A different host is a different site to the browser, and that is what "cross-origin" means.
- path — where on that host. Case matters on most servers.
- query —
?key=value&key2=value2. Filters, searches, tracking. - fragment —
#id. Never sent to the server; the browser scrolls to the element with that id.
https://domvybe.com:443/learn/html/links/?from=home#urls
scheme host port path query fragment
Absolute, root-relative, relative
| Written as | Resolves to | Use when |
|---|---|---|
https://site.com/a/b | Exactly that | Another site |
//site.com/a | The current scheme + that host | Almost never now — just write https |
/about/ | The site root + /about/ | Anywhere in your own site — the safe default |
about/ | The current folder + about/ | You are certain of the current folder |
../about/ | One folder up | Rarely; it breaks as soon as a page moves |
?page=2 | This page with a new query | Pagination and filters |
#top | This page, scrolled | In-page navigation |
/about and /about/ are different URLs. If your page is served from /about/index.html, always link to /about/ — otherwise every relative link inside that page resolves one level too high, and you get a page with broken images that works fine locally.
Anchors inside a page
Give the target an id and link to #that-id. The browser scrolls to it, adds it to the history, and puts it in the address bar so the position can be shared.
Two things make this much better: scroll-margin-top on the target so a sticky header does not cover it, and moving focus, not just the scroll position, so keyboard users continue from the right place.
:target, h2 {
scroll-margin-top: 96px;
}
Opening in a new tab
target="_blank" opens a new tab. Pair it with rel="noopener": without it the opened page receives a reference to yours through window.opener and can navigate it somewhere else — a real phishing route. Modern browsers imply noopener; older ones do not, and the attribute costs nothing.
Do not use it for links inside your own site. You are removing the back button from someone who expected it.
<a href="https://toolrack.app" target="_blank" rel="noopener">
Toolrack <span aria-hidden="true">↗</span>
</a>
rel values worth knowing
| Value | Meaning |
|---|---|
noopener | The new page gets no reference to this one. |
noreferrer | Do not send the Referer header. Implies noopener. |
nofollow | Do not pass ranking credit — for user-submitted or paid links. |
sponsored / ugc | More precise versions of nofollow. |
external | Purely informative, often used as a CSS hook. |
Downloads
download asks the browser to save the file rather than display it, and can rename it. It only works for files on your own origin.
<a href="/downloads/starter.zip" download="domvybe-starter.zip">
Download the starter (1.2 MB, ZIP)
</a>
Say what the file is, how big it is and what format. A link that silently starts a 40 MB download is a bad surprise.
Links you should not make
<a>with nohrefis not a link and is not focusable. If it does something, it is a<button>.<a href="#" onclick="…">navigates to the top of the page if your script fails. Use a button.- A whole card wrapped in one link is fine — but do not nest a second link inside it; that is invalid and unreachable by keyboard.