Project: A Personal Profile Page

Your first complete page. One file, semantic structure, a photograph, a list, a table and a link out - built to a specification and checked against it.

The brief

Build a single page introducing a person: who they are, what they do, how to reach them. One HTML file, no framework, no build step.

Requirements

  • A valid document with a correct head.
  • A page banner containing the name and a short role description.
  • A main region with three sections: about, skills and contact.
  • A photograph with meaningful alt text and dimensions.
  • A skills list marked up as a list.
  • A small table of some kind - languages spoken, or availability by day.
  • At least two external links, opening in a new tab, correctly marked.
  • An email and a telephone link.
  • A footer with a copyright line.
  • Zero validation errors and zero accessibility violations.

Starting structure

<!DOCTYPE html>
<html lang="en-IN">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <title>Meera Iyer - Front End Developer</title>
  <meta name="description" content="Meera Iyer, a front end developer in Pune working on accessible, fast interfaces.">

  <link rel="icon" href="/favicon.svg" type="image/svg+xml">
  <link rel="stylesheet" href="styles/profile.css">
</head>
<body>
  <a href="#main-content" class="skip-link">Skip to main content</a>

  <header>
    <img src="images/meera-iyer.jpg"
         alt="Meera Iyer, smiling, in front of a bookshelf"
         width="200" height="200">
    <h1>Meera Iyer</h1>
    <p>Front end developer, Pune</p>

    <nav aria-label="Sections">
      <ul>
        <li><a href="#about">About</a></li>
        <li><a href="#skills">Skills</a></li>
        <li><a href="#contact">Contact</a></li>
      </ul>
    </nav>
  </header>

  <main id="main-content" tabindex="-1">
    <section>
      <h2 id="about">About</h2>
      <p>I build interfaces that stay usable on a slow connection and an old phone.</p>
      <p>
        I write occasionally about the browser on my
        <a href="https://example.org/journal" target="_blank" rel="noopener noreferrer">
          journal (opens in a new tab)
        </a>.
      </p>
    </section>

    <section>
      <h2 id="skills">Skills</h2>
      <ul>
        <li>Semantic HTML and accessibility</li>
        <li>CSS grid and flexbox layouts</li>
        <li>JavaScript in the browser</li>
      </ul>

      <h3>Languages</h3>
      <table>
        <caption>Languages and level</caption>
        <thead>
          <tr><th scope="col">Language</th><th scope="col">Level</th></tr>
        </thead>
        <tbody>
          <tr><th scope="row">Marathi</th><td>First language</td></tr>
          <tr><th scope="row">English</th><td>Fluent</td></tr>
          <tr><th scope="row">Hindi</th><td>Fluent</td></tr>
        </tbody>
      </table>
    </section>

    <section>
      <h2 id="contact">Contact</h2>
      <dl>
        <div>
          <dt>Email</dt>
          <dd><a href="mailto:meera@example.com">meera@example.com</a></dd>
        </div>
        <div>
          <dt>Phone</dt>
          <dd><a href="tel:+912026001234">+91 20 2600 1234</a></dd>
        </div>
      </dl>
    </section>
  </main>

  <footer>
    <p><small>Written in 2026. Built with HTML and CSS.</small></p>
  </footer>
</body>
</html>

Why each decision was made

DecisionReason
Skip link firstKeyboard users bypass the navigation
tabindex="-1" on mainThe skip link moves focus, not only scroll
One h1, the nameThe page is about this person
Photograph described, not alt=""It is content on a profile page
Ids on the headingsThe in page navigation targets them
Table with both header axesEvery cell is announced with its context
Description list for contactThe data is name and value pairs
New tab warning in the link textNobody is surprised by the back button failing

A minimal stylesheet

:root {
  --text: #1e293b;
  --muted: #475569;
  --accent: #1e3a8a;
  --line: #cbd5e1;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font: 1rem/1.6 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  color: var(--text);
  max-width: 45rem;
  margin-inline: auto;
  padding: 1.5rem 1rem 4rem;
}

img { max-width: 100%; height: auto; border-radius: 50%; }

h1 { font-size: 2rem; margin-bottom: 0.25rem; }
h2 { font-size: 1.375rem; margin-top: 2.5rem; }

a { color: var(--accent); }
a:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

nav ul { list-style: none; display: flex; gap: 1.25rem; padding: 0; }

table { border-collapse: collapse; width: 100%; }
th, td { text-align: left; padding: 0.5rem 0.75rem; border-bottom: 1px solid var(--line); }
caption { text-align: left; font-weight: 600; padding-bottom: 0.5rem; }

dl div { display: grid; grid-template-columns: 6rem 1fr; gap: 0.5rem; padding: 0.35rem 0; }
dt { font-weight: 600; }
dd { margin: 0; }

.skip-link { position: absolute; left: -9999px; }
.skip-link:focus { left: 1rem; top: 1rem; background: #fff; padding: 0.75rem 1rem; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

Checking your work

  1. Validate. Zero errors.
  2. Tab through the whole page. Everything reachable, focus always visible, skip link working.
  3. Turn images off. Does the alt text carry the meaning?
  4. Narrow the window to 320 pixels. No horizontal scrolling.
  5. Zoom to 200 percent. Nothing clipped or overlapping.
  6. Run an accessibility scan. Zero violations.
  7. Run the console audit snippets from the debugging note.

Extensions

  • Add a dark mode with prefers-color-scheme.
  • Add Open Graph tags and check the preview card.
  • Add Person structured data with JSON-LD.
  • Add a details element holding a longer biography.
  • Print the page. Add a print stylesheet that hides the navigation.

What this teaches

Everything in this project is a decision you will make on every page you ever build: which element carries which meaning, where the heading levels sit, what the alt text says, and how a keyboard moves through it. A profile page is small enough to get all of it right, which is the point.

Practice

  1. Build the page for yourself with your own content.
  2. Add a fourth section and keep the navigation and heading structure correct.
  3. Replace the table with a description list and explain which fits better.
  4. Ask someone else to navigate it with the keyboard and watch where they hesitate.

Useful resources

Hand picked references for this topic
Written by Lorens Mishra

Default administrator account created by the installer.

Continue reading

All HTML notes →
HTML

Project: A Portfolio Page

A grid of project cards with a filter, responsive images and a case study page. The project where card markup, image performance and clickable regions...

Read more
HTML

Project: A Product Page

An image gallery, a specification list, a quantity form and structured data. The page where hidden fields, tables and forms all have to be right at on...

Read more
HTML

Project: A Resume Page

A document with real structure: dated entries, an experience timeline, skills grouped by category, and a print stylesheet that produces a usable PDF.

Read more

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.