FullStack Course LogoFullStack Course
Module: HTML
HTML·004·10 MIN READ

004: Links and Navigation

TOPICS COVERED: Links and Navigation

Learning outcomes

By the end of this lesson, you can create useful hyperlinks with a and href, resolve relative paths from the current file, distinguish absolute URLs and fragments, use mailto: and tel: appropriately, and build consistent list-based navigation for a three-page website.

Prerequisites and retrieval

Start with the valid profile article from 003. Before you write a link, explain the roles of a URL's scheme, host, path, query, and fragment. Then draw the portfolio folder as a tree. Seeing where each file lives makes relative links much easier to reason about.

Terminology

  • Hyperlink: A link created by an anchor with href that navigates users to another resource. — Source: WHATWG: Links
  • Anchor: “The a element … if it has an href attribute, then it represents a hyperlink.” — Source: WHATWG: The a element
  • Destination: The URL resolved from a link's href attribute — where navigation goes. — Source: WHATWG: Links
  • Absolute URL: A complete URL including scheme and host, such as https://example.com/about. — Source: WHATWG URL Living Standard: URLs
  • Relative URL: A URL without scheme/host that is resolved against a base URL. — Source: WHATWG URL: Relative URL
  • Fragment: The part of a URL beginning with # identifying a section within the resource. — Source: WHATWG URL: Fragment
  • Root-relative URL: A path starting with “/” resolved from the site origin; formally a path-absolute URL string. — Source: WHATWG URL: Path-absolute
  • Navigation: A major group of links for moving through the site, typically wrapped in nav. — Source: WHATWG: The nav element
  • Link text: The visible words of a link describing destination or purpose; also its accessible name. — Source: WCAG 2.2: Link Purpose (In Context)
  • URL: "A URL is a string that identifies a resource and the mechanism to access it, composed of scheme, host, port, path, query, and fragment." — Source: WHATWG URL Living Standard
  • Origin: "An origin is the tuple (scheme, host, port) that identifies the trust boundary for a document." — Source: WHATWG URL: Origin
  • Base URL: "The URL against which a relative URL is resolved." — Source: WHATWG URL: Base URL
  • Percent-encoding: "Encoding of reserved characters as % followed by two hex digits." — Source: WHATWG URL: Percent-encode

Mental model: directions from where you stand

Think of an absolute URL as a complete postal address. A relative URL is more like a direction from your current position: “open the file beside me,” “enter this folder,” or “go up one folder.” The direction itself has not changed, but it can lead somewhere different when the starting file changes.

Given:

text
portfolio/
├─ index.html
├─ about.html
├─ contact.html
└─ projects/
   └─ weather.html

From index.html, about.html names a sibling file. From projects/weather.html, the same text means projects/about.html, which is the wrong location. Use ../about.html to go up one folder first. URL separators are forward slashes, even when you are working on Windows.

The smallest useful link looks like this:

html
<a href="about.html">About Asha</a>

The element's content becomes the link's accessible name. It should tell a person what the destination is or what will happen in context. “Read Asha's biography” gives a user more information than “click here,” and it remains meaningful when the link appears in a list. A long URL should not normally be pasted in as link text unless recognizing that exact URL is useful.

The useful distinction here is action versus navigation. Links take users to a resource; buttons perform an action on the current page, such as submitting a form. Do not turn an anchor without href into a button, and do not use a button just to navigate to another document.

Destination forms

These examples cover the destination forms you will use most often:

html
<a href="https://developer.mozilla.org/">MDN Web Docs</a>
<a href="projects/weather.html">Weather project</a>
<a href="../index.html">Home</a>
<a href="#contact">Jump to contact details</a>
<a href="about.html#skills">Asha's skills</a>
<a href="mailto:asha@example.com">Email Asha</a>
<a href="tel:+15550123456">Call +1 555 012 3456</a>

mailto: and tel: ask a configured application to handle the destination. They do not guarantee that an email is sent or that a call is placed. A public email address may also attract spam. For phone links, show a number people can read while using an international, machine-friendly value where that is appropriate.

A fragment points into a document, so it needs a matching and unique id:

html
<h2 id="skills">Skills</h2>

IDs must be unique within the document. Keep them stable, concise, and free of spaces. Depending on the target and user agent, the browser can scroll to the element and move focus as part of fragment navigation. An empty link is not a spacing tool; do not create one just to affect layout.

A link can request behavior beyond ordinary navigation, but that behavior should solve a real problem rather than surprise the user.

html
<a href="report.pdf" download>Download the report</a>
<a href="https://external.example/guide" target="_blank" rel="noopener">Open external guide in a new tab</a>
  • download suggests that the target should be downloaded instead of opened for navigation. Browser and cross-origin restrictions can determine whether the suggestion is honored.
  • target="_blank" opens a new browsing context. Do not add it automatically: an unexpected new tab can disorient users. If you use it, the link's context should make that behavior clear.
  • rel describes the relationship between the current document and the linked resource. Values such as noopener, noreferrer, nofollow, prev, and next each have a particular purpose.

Many modern browsers treat _blank links as though noopener were present. Writing it explicitly can still make the security intent clear in maintained code. noreferrer goes further by suppressing referrer information, which changes analytics and server behavior, so it should not be added casually.

Treat the URL in href as data. If code builds a destination from untrusted input, validate it before putting it into markup. Avoid javascript: URLs; use real links for navigation and buttons for actions.

Guided example: create three pages

Create index.html, about.html, and contact.html as sibling files. Give each page the 002 skeleton, a unique title, one h1, and the same navigation:

html
<header>
  <p>Asha Rao</p>
  <nav aria-label="Primary">
    <ul>
      <li><a href="index.html">Home</a></li>
      <li><a href="about.html">About</a></li>
      <li><a href="contact.html">Contact</a></li>
    </ul>
  </nav>
</header>

nav identifies a major navigation region, while the list groups the destinations as a set. aria-label="Primary" names this region, which helps when a page has more than one navigation region. It is a restrained use of ARIA, not a substitute for nav; when there is one obvious navigation region, the label may be omitted.

Here is a complete home page to use as the starting point:

html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Home | Asha Rao</title>
  </head>
  <body>
    <header>
      <p>Asha Rao</p>
      <nav aria-label="Primary">
        <ul>
          <li><a href="index.html">Home</a></li>
          <li><a href="about.html">About</a></li>
          <li><a href="contact.html">Contact</a></li>
        </ul>
      </nav>
    </header>
    <main>
      <h1>Building clear, accessible interfaces — one semantic page at a time</h1>
      <p>I am building a portfolio one semantic page at a time.</p>
      <p><a href="about.html#skills">Explore my current skills</a></p>
    </main>
  </body>
</html>

On about.html, add <h2 id="skills">Current skills</h2>. On the contact page, add visible email and phone links. Test every link from every page, not just the links on Home. The address bar is useful here: it lets you see the fragment when the browser navigates to the skills heading.

A link to the current page may remain a normal link, allowing users to return to the top. Later, you can identify the current item with aria-current="page". Apply it only to the current item and update it separately on each page; it does not belong on every link.

Intermediate example: add a nested project

Now create projects/weather.html. The page's navigation is the same in meaning, but every path must start from the nested file's location:

html
<nav aria-label="Primary">
  <ul>
    <li><a href="../index.html">Home</a></li>
    <li><a href="../about.html">About</a></li>
    <li><a href="../contact.html">Contact</a></li>
  </ul>
</nav>

From Home, link to it with projects/weather.html. Trace the two paths on the folder tree rather than guessing. For this local exercise, avoid /projects/weather.html: a leading slash behaves differently with file: URLs and when the site is hosted at https://host.example/my-portfolio/ instead of the origin root.

For an external project repository, use an absolute URL:

html
<a href="https://github.com/example/weather-project">Weather project source code</a>

Opening that repository in a new tab is not automatically better. It can disorient users and takes away their choice of browsing context. If a real requirement calls for target="_blank", make the new context clear in the link's wording or surrounding context. Modern browsers imply noopener for _blank, although an explicit project policy may still require rel="noopener"; do not add target casually.

Advanced optional extension: meaningful navigation states

On Home only, mark the current navigation item like this:

html
<a href="index.html" aria-current="page">Home</a>

The link remains a native link and still navigates. ARIA adds the information that this is the current page. On About, move aria-current to About. This is an enhancement to correct native HTML, not a replacement for it. Likewise, do not add role="navigation" to nav or role="link" to a; those roles duplicate native semantics.

Add a footer navigation with its own label only when it is genuinely useful:

html
<nav aria-label="Legal">
  <ul>
    <li><a href="privacy.html">Privacy</a></li>
  </ul>
</nav>

Distinct labels help users of landmark navigation tell the two navigation regions apart.

Common mistakes and debugging

When a link fails, start with the document that contains it and resolve the path from there. These are the failures worth checking first:

  • Backslashes in URLs: use /, not \.
  • Wrong starting point: resolve each relative URL from the document containing the link.
  • Filename case mismatch: deployed servers may treat About.html and about.html differently.
  • Spaces and unstable names: prefer lowercase names such as weather-project.html.
  • Broken fragment: verify the target id character for character and make sure it is unique.
  • href="#" placeholder: it unexpectedly jumps to the top; use a real destination or leave the unfinished control out.
  • “Click here” repeated: rewrite the text so it describes the destination or action.
  • Confusing link and button: navigation uses a[href]; actions use button.
  • Testing only locally: verify the paths from the deployed base path as well.

Accessibility, security, and performance

Links need to work from the keyboard and have an understandable purpose. A native anchor with href supplies focus and activation behavior automatically. Keep the order and wording of navigation consistent across pages, which supports WCAG Consistent Navigation. If you style links in HTML/CSS work, do not remove underlines or focus indicators unless you provide an accessible alternative.

An external URL can disclose the current page through referrer information, and secrets should never be put in URLs. mailto: also exposes an address publicly; if spam or privacy becomes a concern, a server-side contact form may be more appropriate later. Broken links waste users' time and can cause unnecessary requests, so include link checking in project review. Fragment navigation is fast because it does not require a new document request.

Tiered exercises

Level 1: paths

From projects/weather.html, write links to root index.html, sibling projects/gallery.html, and the #results section of the current page.

Level 2: site

Build Home/About/Contact pages with consistent list navigation, distinct titles and headings, an About skills fragment, and meaningful email/phone links.

Level 3: audit

Add one nested project page, mark the current navigation item, then keyboard-test and classify every link as internal, external, fragment, email, or telephone.

Level 1: <a href="../index.html">Home</a>, <a href="gallery.html">Gallery project</a>, and <a href="#results">Project results</a>.

Level 2: use the guided Home document on every page with a page-specific title, h1, and body. About includes <h2 id="skills">Current skills</h2>. Contact includes <a href="mailto:asha@example.com">asha@example.com</a> and <a href="tel:+15550123456">+1 555 012 3456</a>. Every navigation URL is correct because the files are siblings.

Level 3: nested project navigation uses ../. Exactly one primary navigation link per page has aria-current="page". A keyboard test reaches links in source order with visible browser focus and activates them with Enter. External links remain in the same tab unless a documented requirement says otherwise.

Recap and exit questions

Links connect resources, and a relative destination is resolved from the current file. Link text communicates purpose, fragments target unique IDs, and a list inside nav organizes major destinations.

  1. From which location is a relative URL resolved?
  2. Why does ../about.html work from a nested page?
  3. When should you use a button instead of a link?
  4. What is one privacy concern with mailto:?
  5. Why should new-tab behavior be exceptional?

Try it with your own example

Relative paths tend to become clear after one controlled failure. Break a link deliberately before you depend on this skill in a real project.

Give Rina's site this structure:

text
rinas-kitchen/
├─ index.html
├─ menu.html
├─ order.html
└─ menu/
   └─ sourdough.html

From menu/sourdough.html, write a link back to the main menu and another link for placing an order. First make the menu link wrong on purpose:

html
<a href="menu.html">Back to full menu</a>

Open menu/sourdough.html directly in the browser and click the link. It fails because, from inside menu/, menu.html means menu/menu.html, which does not exist. Fix it yourself before looking at the answer:

html
<a href="../menu.html">Back to full menu</a>
<a href="../order.html">Order this loaf</a>

This is the common path bug in miniature: a link works while all files share one folder, then breaks as soon as the project gains a subfolder. Causing it once on a page nobody depends on gives you a concrete debugging reference, rather than a rule you have only memorized.

Further reading: MDN — File paths covers the same ../ mechanics with a different folder example if you want a second worked case.

Official references

Reader page: /html/lesson/004/links-and-navigation