Skip to content

Web Foundations and Front-End Roles

Step 2 of 45

A Semantic First Webpage

Introduction

This lesson belongs to course 1 of the Meta Front-End Developer pathway and teaches the foundation everything else rests on: HTML that describes what content is, and CSS that describes how it looks. React components, which come later, ultimately produce HTML. If your HTML is wrong, no framework will rescue it, and the people who suffer most are those using screen readers, keyboards, or search engines.

In this lesson you build the first page of the Harbor Workshops project: an accessible tutor profile for a community workshop. You will not copy a course assignment; the brief below is original to OnLevelUp.

After this lesson you will be able to write a well-structured HTML document, choose elements for their meaning, write useful alternative text, and apply the basics of the CSS cascade and box model.

Learning objectives

  1. Write a complete HTML5 document with correct boilerplate.
  2. Choose headings, lists, links, and landmark elements by meaning, not appearance.
  3. Write alternative text appropriate to an image's purpose.
  4. Explain the CSS cascade: origin, specificity, and source order.
  5. Predict the size of a box using the box model and box-sizing.
  6. Build and review a small accessible profile page.

1. The HTML document

HTML (HyperText Markup Language) wraps content in elements. An element has an opening tag, content, and a closing tag. Elements can have attributes, name/value pairs inside the opening tag.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Maya Chen, Pottery Tutor | Harbor Workshops</title>
    <link rel="stylesheet" href="/styles.css">
  </head>
  <body>
    <h1>Maya Chen</h1>
    <p>Pottery tutor at Harbor Workshops.</p>
  </body>
</html>

What each piece does:

Piece Purpose
<!doctype html> Puts the browser in standards mode
lang="en" Declares the page language; screen readers choose pronunciation from it, and translation tools rely on it
charset="utf-8" Makes accented characters and symbols display correctly
viewport meta Lets mobile browsers use the real device width (more in lesson 1.3)
<title> Appears in the tab, in search results, and is the first thing a screen reader announces

The <head> contains information about the page. The <body> contains what visitors see. Browsers are forgiving about mistakes and quietly repair them, so you cannot judge correctness by appearance. Use the W3C Nu HTML Checker to validate.

2. Semantic elements: meaning over appearance

A semantic element communicates its role. <button> says "this is clickable and does something". A <div> says nothing. Browsers, search engines, and assistive technologies use meaning to build navigation tools: a screen-reader user can jump from heading to heading, list to list, or landmark to landmark.

Headings

Headings <h1> to <h6> build the outline of the page. Rules of thumb:

  • One <h1> per page, describing the page's main topic.
  • Do not skip levels (<h2> then <h4>) because you like the font size. Use CSS for size.
  • Headings are not for making text bold.

Text, lists, and links

<p>Maya has taught ceramics for <strong>twelve years</strong>.</p>

<h2>Classes this term</h2>
<ul>
  <li>Wheel basics (Mondays)</li>
  <li>Glazing workshop (Saturdays)</li>
</ul>

<h2>Preparation steps</h2>
<ol>
  <li>Wear clothes you can get dirty.</li>
  <li>Arrive ten minutes early.</li>
  <li>Bring a towel.</li>
</ol>

<p>Read the <a href="/workshops/pottery">pottery workshop details</a>.</p>
  • <ul> is an unordered list; <ol> is ordered, so use it when sequence matters.
  • <strong> marks strong importance; <em> marks emphasis. Screen readers may change tone. <b> and <i> are for stylistic offsets without added importance.
  • Link text must make sense out of context. Screen-reader users often list all links on a page. "Click here" repeated five times is useless; "pottery workshop details" is meaningful.
  • Links navigate; buttons act. If clicking changes the page's URL, use <a>. If it submits or toggles something, use <button>.

Landmarks

Landmark elements divide the page into regions that assistive technology can jump between.

Element Role Use
<header> Introductory content Site logo and title (or the intro of a section)
<nav> Navigation links Main menu; label it with aria-label if you have several
<main> Primary content Exactly one per page
<section> A thematic group, with a heading Classes, reviews
<article> Self-contained content A workshop card, a blog post
<aside> Related but secondary Tips, side notes
<footer> Closing content Contact info, legal links

Overusing <div> is the classic beginner pattern called "div soup". A <div> is fine when no semantic element fits, typically for grouping elements for layout.

Worked example 1: converting div soup

Problem. A previous developer wrote this:

<div class="top">
  <div class="big">Harbor Workshops</div>
  <div class="links">
    <span onclick="go('home')">Home</span>
    <span onclick="go('classes')">Classes</span>
  </div>
</div>
<div class="main">
  <div class="big">Maya Chen</div>
  <div>Pottery tutor</div>
</div>

Visually it works. Convert it to semantic HTML.

Reasoning, step by step.

  1. The top block is introductory content with the site name and navigation: use <header>, with the site name in a paragraph or heading and a <nav> for links.
  2. The "links" are <span>s with click handlers. A span is not focusable by keyboard and is not announced as a link. Each destination is a new page, so use <a href>.
  3. The main block is the primary content: <main>. "Maya Chen" is the page's topic: <h1>.
  4. "Pottery tutor" is a paragraph: <p>.

Solution.

<header>
  <p class="site-name">Harbor Workshops</p>
  <nav aria-label="Main">
    <ul>
      <li><a href="/">Home</a></li>
      <li><a href="/classes">Classes</a></li>
    </ul>
  </nav>
</header>
<main>
  <h1>Maya Chen</h1>
  <p>Pottery tutor</p>
</main>

Why other options are worse. Adding role="link" and tabindex="0" to the spans would recreate part of an anchor's behavior by hand, still without right-click, middle-click, or status-bar previews. The first rule of ARIA is: if a native element does the job, use it.

3. Images and alternative text

<img src="/img/maya-wheel.jpg" alt="Maya guiding a student's hands on a spinning clay pot" width="640" height="427">
  • alt is required. It is read by screen readers and displayed when the image fails to load.
  • Write alt text according to the image's purpose:
Image role Example Alt text
Informative Photo of the studio Describe what matters: "Studio with six pottery wheels and shelves of glazed bowls"
Functional Icon inside a link or button Describe the action: "Search" not "magnifying glass"
Decorative Ornamental divider Empty alt, alt="", so assistive technology skips it
Complex Chart Short alt plus a full description nearby in text
  • Do not start with "image of" or "picture of"; screen readers already announce an image.
  • Always give width and height attributes (or an equivalent CSS aspect ratio). The browser reserves space before the image loads, preventing the page from jumping, which is called layout shift.
  • Use loading="lazy" for images far below the fold.

4. CSS basics

CSS (Cascading Style Sheets) is a set of rules. Each rule has a selector and declarations.

h1 {
  color: #1f3a5f;
  font-size: 2rem;
}

You can attach CSS in three ways: an external file via <link> (preferred, cacheable), a <style> element, or a style attribute (hard to override, avoid).

Selectors

Selector Example Matches
Type p All paragraphs
Class .card Elements with class="card"
ID #intro The element with id="intro" (IDs must be unique)
Descendant nav a Any link inside a nav
Child ul > li Direct children only
Attribute a[target="_blank"] Links that open a new tab
Pseudo-class a:hover, button:focus-visible A state

Prefer classes for styling; they are reusable and have moderate specificity.

The cascade

When several rules target the same property on the same element, the browser chooses a winner in this order:

  1. Importance and origin: !important declarations win; author styles beat the browser's default styles.
  2. Specificity: a more specific selector wins. Roughly, inline style > IDs > classes/attributes/pseudo-classes > type selectors.
  3. Source order: if everything else ties, the later rule wins.
p { color: black; }
.note { color: darkgreen; }
p.note { color: navy; }

For <p class="note">, all three match. p has specificity (0,0,1), .note is (0,1,0), p.note is (0,1,1). The result is navy. Reordering the rules would not change it, because specificity is compared before order.

Inheritance: some properties (color, font-family, line-height) pass from parent to children; others (border, margin, padding) do not. This is why setting font-family on body styles the whole page.

Units

  • px is an absolute pixel.
  • rem is relative to the root font size (usually 16px). Prefer rem for text and spacing; users who increase their default font size are respected.
  • em is relative to the element's own font size.
  • % is relative to the parent.

The box model

Every element is a rectangular box: content, then padding, then border, then margin (outside space).

By default (box-sizing: content-box), width sets only the content width. Consider:

.card {
  width: 300px;
  padding: 20px;
  border: 5px solid #ccc;
}

Total horizontal space: 300 + 2×20 + 2×5 = 350px. With box-sizing: border-box, width includes padding and border, so the card is exactly 300px wide and the content shrinks to 250px. Most projects start with:

*, *::before, *::after {
  box-sizing: border-box;
}

Vertical margins between adjacent block elements can collapse: a 20px bottom margin above a 30px top margin results in a 30px gap, not 50px.

Block and inline

Block elements (div, p, h1, section) start on a new line and fill the width. Inline elements (a, strong, span, img) flow within a line and ignore width and height (for non-replaced ones). display: inline-block combines behaviors, and display: none removes an element from layout and from the accessibility tree.

5. Building the tutor profile page

Brief (original). Harbor needs a profile page for tutor Maya Chen with: her name, a photo, a short biography, a list of classes, and a "Book a class" link. It must work with the keyboard, have good contrast, and look fine at 360px width.

index.html:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Maya Chen, Pottery Tutor | Harbor Workshops</title>
  <link rel="stylesheet" href="/styles.css">
</head>
<body>
  <a class="skip-link" href="#content">Skip to main content</a>
  <header>
    <p class="site-name">Harbor Workshops</p>
    <nav aria-label="Main">
      <ul class="nav-list">
        <li><a href="/">Home</a></li>
        <li><a href="/tutors" aria-current="page">Tutors</a></li>
      </ul>
    </nav>
  </header>
  <main id="content">
    <article class="profile">
      <h1>Maya Chen</h1>
      <img src="/img/maya.jpg" alt="Maya smiling beside a shelf of glazed bowls" width="320" height="320">
      <p>Maya has taught ceramics for twelve years and loves helping first-timers shape their first bowl.</p>
      <h2>Classes this term</h2>
      <ul>
        <li>Wheel basics (Mondays, 18:00)</li>
        <li>Glazing workshop (Saturdays, 10:00)</li>
      </ul>
      <a class="button" href="/book?tutor=maya">Book a class</a>
    </article>
  </main>
  <footer><p>Harbor Workshops, Quay Street 12</p></footer>
</body>
</html>

styles.css:

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  font-family: system-ui, sans-serif;
  line-height: 1.5;
  color: #1a1a1a;
}

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

header, main, footer { padding: 1rem; }
.nav-list { display: flex; gap: 1rem; list-style: none; padding: 0; }

.profile { max-width: 40rem; margin: 0 auto; }
.profile img { max-width: 100%; height: auto; border-radius: .5rem; }

.button {
  display: inline-block;
  padding: .75rem 1.25rem;
  background: #1f3a5f;
  color: #fff;
  text-decoration: none;
  border-radius: .25rem;
}
.button:hover, .button:focus-visible { background: #14263f; }
a:focus-visible { outline: 3px solid #e08a00; outline-offset: 2px; }

Notes on decisions:

  • The skip link lets keyboard users bypass the navigation. It is hidden off-screen but becomes visible on focus. Do not use display: none for it, because it would then never receive focus.
  • max-width: 100%; height: auto keeps the image inside narrow screens.
  • The visible focus outline is retained and made clearer. Removing it with outline: none and no replacement is an accessibility failure.
  • #1f3a5f on white has a contrast ratio well above 4.5:1, the WCAG AA minimum for normal text. White text on the button gives a similar ratio.
  • aria-current="page" tells assistive technology which link is the current page.

Predicted behavior. At 360px width the nav items sit in a row, the image shrinks to the container, and the button remains easy to tap. Pressing Tab first reaches the skip link, then "Home", "Tutors", and "Book a class", each with a visible outline.

6. Common pitfalls and exam tips

  • Using headings for styling or skipping levels.
  • Using <br> for spacing between paragraphs (use margin).
  • Placing block elements inside <p>, which the parser auto-closes.
  • Missing alt or writing "image of…".
  • Removing focus outlines.
  • Relying on color alone to convey meaning (for example, red text for errors without an icon or text).
  • Setting fixed pixel heights that clip text when users enlarge fonts.
  • Selectors that are too specific (#page .content ul li a) and hard to override. Prefer a single class.

Exam tip. When asked which element to use, choose the one whose meaning matches the content. "Which is best for a set of related links?" is <nav> containing a list. "Which text is read by screen readers when an image fails?" is alt.

Exam tip. To compute total box width, add content, padding, border (both sides), and margin where relevant, and note the box-sizing value first.

Mini case study: the clickable span

Situation. A usability test shows that a keyboard-only participant cannot reach the "Book a class" control on a draft profile. The control is <span class="button" onclick="book()">Book a class</span>. Also, the photo has alt="image" and the page has two <h1> elements.

Task. List the problems and propose fixes.

Solution.

  1. Span with click handler. A span is not in the tab order and exposes no role. If booking navigates to a new page, use <a href="/book?tutor=maya">; if it triggers an action in the page, use <button type="button">. Both are focusable and activated by keyboard natively.
  2. alt="image". It conveys nothing. Replace it with a purposeful description such as "Maya smiling beside a shelf of glazed bowls".
  3. Two <h1>s. Keep one <h1> for the page topic (the tutor name); demote the other to <h2>, adjusting size with CSS.

Check. Tab reaches the control; a screen reader announces "link, Book a class" (or "button"); the outline lists one <h1> followed by <h2>s.

Key terms

  • Element / attribute: building block of HTML / extra information on an element.
  • Semantic HTML: elements chosen for meaning.
  • Landmark: region (header, nav, main, footer) used for navigation by assistive tech.
  • Alt text: text alternative for an image.
  • Selector / declaration: what a CSS rule targets / the property-value pair.
  • Cascade / specificity: how conflicting rules are resolved.
  • Box model: content, padding, border, margin.
  • Focus: the element currently receiving keyboard input.

Summary checklist

  • My document has doctype, lang, charset, viewport, and a descriptive title.
  • I used one <h1>, no skipped heading levels, and landmark elements.
  • Links have meaningful text and buttons are real buttons.
  • Every image has a purposeful alt (empty if decorative) and dimensions.
  • I can predict which of several CSS rules wins and why.
  • I can calculate box width with and without border-box.
  • Keyboard focus is visible and the page is usable by Tab alone.

Official sources