Step 02 of 21

HTML essentials

Write well-structured, semantic HTML pages with a sound heading outline and forms that validate and submit correctly.

16 min 8 sections

HTML describes what each piece of a page is: a heading, a navigation menu, a list, a form field. Browsers, search engines, screen readers and your own CSS and JavaScript all rely on that description, so getting it right makes everything downstream easier. In this lesson you will build the skeleton every page needs, structure content with semantic elements and headings, and write forms that work before a single line of JavaScript.

The skeleton of every page

Here is a complete, modern HTML document. Every line earns its place:

html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Trail Shoes – Ridgeway Outdoors</title>
    <link rel="stylesheet" href="/styles.css">
    <script src="/app.js" defer></script>
  </head>
  <body>
    <h1>Trail shoes</h1>
    <p>Grippy, light and waterproof.</p>
  </body>
</html>
  • <!doctype html> switches the browser into standards mode. Without it you get "quirks mode", which emulates bugs from the 1990s.
  • lang="en" tells screen readers which voice to use and lets the browser offer translation.
  • <meta charset="utf-8"> must come early so characters like é and – display correctly.
  • The viewport meta tag makes mobile browsers use the device width instead of pretending to be a desktop screen.
  • <title> appears in the tab, bookmarks and search results. Put the specific page first and the site name last.
  • <head> holds information about the page; <body> holds what the user sees.

Elements, attributes and nesting

An element is usually an opening tag, content, and a closing tag: <p>Hello</p>. Attributes go in the opening tag and configure the element: <a href="/about">. Elements nest like boxes and must close in reverse order – <p><strong>Hi</strong></p>, never <p><strong>Hi</p></strong>.

Void elements have no content and no closing tag: <img>, <input>, <br>, <meta>, <link>, <hr>.

To show characters HTML treats specially, use character references: &lt; for <, &gt; for >, and &amp; for &.

Semantic structure

You could build an entire page out of <div> and <span>, and it would look fine once styled. But a <div> means nothing. Semantic elements carry meaning that the browser exposes to assistive technology, search engines and browser features like reader mode.

ElementUse it for
<header>Introductory content: logo, site title, sometimes the main nav.
<nav>A block of major navigation links.
<main>The page's primary content. Use exactly one visible <main> per page.
<article>Self-contained content that would make sense on its own: a blog post, a product card, a comment.
<section>A thematic group of content, normally with its own heading.
<aside>Content tangential to the main content: a sidebar, related links.
<footer>Closing content: copyright, contact links.

A typical page layout looks like this:

html
<body>
  <header>
    <a href="/">Ridgeway Outdoors</a>
    <nav>
      <ul>
        <li><a href="/shoes">Shoes</a></li>
        <li><a href="/jackets">Jackets</a></li>
      </ul>
    </nav>
  </header>

  <main>
    <h1>Trail shoes</h1>
    <article>
      <h2>Ridge Runner 3</h2>
      <p>Our lightest waterproof shoe.</p>
    </article>
  </main>

  <footer>
    <p>&copy; 2026 Ridgeway Outdoors</p>
  </footer>
</body>

Screen reader users can jump straight between these regions (called landmarks), the same way sighted users glance at a page's layout.

Text-level semantics

Inside those regions, pick elements for meaning, not appearance:

  • <p> for paragraphs, <ul> for unordered lists, <ol> for ordered steps, each item in an <li>.
  • <strong> for importance and <em> for stress emphasis. <b> and <i> only style text without adding meaning.
  • <figure> with <figcaption> for an image, chart or code sample with a caption.
  • <time datetime="2026-03-14">14 March</time> for machine-readable dates.
  • <code> for inline code, <blockquote> for quotations.

Headings and the document outline

Headings <h1> to <h6> form the outline of your page – a table of contents that screen reader users navigate by and search engines use to understand structure. The rules are simple:

  1. Give the page one <h1> describing its main topic.
  2. Nest levels logically: an <h3> belongs under an <h2>. Do not skip from <h2> to <h4>.
  3. Choose the level by structure, never by font size. Size is CSS's job.

A product page might produce this outline:

text
h1 Trail shoes
  h2 Ridge Runner 3
    h3 Specifications
    h3 Reviews
  h2 Summit Grip
    h3 Specifications

<a href="…"> navigates somewhere. The href can be absolute (https://example.com/help), root-relative (/help), relative to the current page (help.html), a fragment on the same page (#reviews), or a scheme like mailto:. Link text should make sense on its own – "Download the size guide", not "click here".

target="_blank" opens a new tab. Modern browsers automatically apply rel="noopener" behaviour to it, but use it sparingly: unexpected new tabs disorient people.

Images

html
<img src="/img/ridge-runner.jpg"
     alt="Grey trail shoe with an orange sole"
     width="800" height="600">

alt text replaces the image when it cannot be seen: describe what the image communicates. For purely decorative images, write alt="" so screen readers skip them – omitting alt entirely makes some screen readers read out the file name. Setting width and height lets the browser reserve space before the image loads, so the page does not jump.

If it goes somewhere, it is a link. If it does something – opens a menu, submits a form, adds to the basket – it is a <button>. A <div> with a click handler is neither: it cannot be focused with the keyboard or activated with Enter or Space without extra work.

Forms

Forms are how users send data to a server, and HTML does a lot of the work for you.

html
<form action="/signup" method="post">
  <label for="email">Email</label>
  <input id="email" name="email" type="email" autocomplete="email" required>

  <label for="password">Password</label>
  <input id="password" name="password" type="password"
         minlength="12" autocomplete="new-password" required>

  <fieldset>
    <legend>Plan</legend>
    <label><input type="radio" name="plan" value="free" checked> Free</label>
    <label><input type="radio" name="plan" value="pro"> Pro</label>
  </fieldset>

  <label>
    <input type="checkbox" name="newsletter" value="yes"> Send me the newsletter
  </label>

  <button type="submit">Create account</button>
</form>

How the pieces fit

  • action is the URL that receives the data; method is get or post.
  • name is the key the field's value is sent under. A field without a name is not submitted at all.
  • <label> connects text to a field, either with for matching the input's id or by wrapping the input. Clicking the label focuses the field, and screen readers announce the label when the field is focused. Placeholder text is not a substitute.
  • Radio buttons in a group share one name; only the checked one's value is sent.
  • <fieldset> and <legend> group related controls and give the group a name.
  • Checkboxes send their value only when checked (the default value is on). Unchecked boxes send nothing.

With method="get", the browser puts the data in the URL's query string, which suits searches and filters:

text
/search?q=trail+shoes&size=42

With method="post", the data goes in the request body, which suits anything that changes state or contains private data.

Useful input types

TypeWhat you get
email, urlFormat validation and a suitable mobile keyboard
telA phone keypad (no format validation – formats vary worldwide)
numberNumeric input with min, max and step
dateA native date picker
passwordMasked characters
searchA search field, often with a clear button

For longer text use <textarea name="message" rows="5"></textarea>, and for a fixed list of options use <select name="size"> with <option value="42">42</option> children.

Built-in validation

Attributes like required, minlength, maxlength, min, max, pattern and type="email" make the browser block submission and show a message when a value is invalid:

html
<input name="username" required pattern="[A-Za-z0-9]{3,16}"
       title="3–16 letters or numbers">

Common pitfalls

  • Buttons inside forms default to type="submit". A "Show password" button without type="button" will submit the form.
  • Skipping heading levels for styling breaks the outline. Pick the right level and resize it with CSS.
  • Missing name attributes – the form "submits" but the server receives nothing.
  • Duplicate id values – for and id must be unique on the page, or labels attach to the wrong field.
  • Using <br> for spacing – use separate elements and let CSS handle spacing.
  • Putting block elements inside a <p>, such as a <div> or a list. A paragraph can only contain text-level content, so the browser silently closes the <p> early and builds a different structure than you wrote.

Practice

Build a contact.html page for a small bakery, from scratch, in a single file. It needs:

  1. A correct document skeleton with lang, charset, viewport and a descriptive <title>.
  2. A site header with the bakery name and a <nav> of three links (Home, Menu, Contact).
  3. A <main> with an <h1> and a short intro paragraph.
  4. A contact form posting to /contact with: name (required), email (required, validated), a topic <select> (Order, Catering, Other), a message <textarea> (required, at least 20 characters), and a submit button.
  5. A footer with an opening-hours list.

Open it in a browser and try submitting it empty, with a bad email, and with a 10-character message.

Show solution
html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Contact us – Crumb &amp; Co. Bakery</title>
  </head>
  <body>
    <header>
      <a href="/">Crumb &amp; Co.</a>
      <nav>
        <ul>
          <li><a href="/">Home</a></li>
          <li><a href="/menu">Menu</a></li>
          <li><a href="/contact" aria-current="page">Contact</a></li>
        </ul>
      </nav>
    </header>

    <main>
      <h1>Contact us</h1>
      <p>Questions about an order or catering? We reply within one working day.</p>

      <form action="/contact" method="post">
        <label for="name">Your name</label>
        <input id="name" name="name" autocomplete="name" required>

        <label for="email">Email</label>
        <input id="email" name="email" type="email" autocomplete="email" required>

        <label for="topic">Topic</label>
        <select id="topic" name="topic">
          <option value="order">Order</option>
          <option value="catering">Catering</option>
          <option value="other">Other</option>
        </select>

        <label for="message">Message</label>
        <textarea id="message" name="message" rows="5" minlength="20" required></textarea>

        <button type="submit">Send message</button>
      </form>
    </main>

    <footer>
      <h2>Opening hours</h2>
      <ul>
        <li>Tuesday–Friday: 7:00–16:00</li>
        <li>Saturday: 8:00–14:00</li>
      </ul>
    </footer>
  </body>
</html>

Submitting empty highlights the first required field; a bad email triggers the email format message; a short message reports the minlength requirement. aria-current="page" marks the current page's link for screen readers.

Key takeaways

  • Every page needs a doctype, lang, a UTF-8 charset, a viewport meta tag and a specific <title>.
  • Choose elements for meaning: landmarks like <header>, <nav>, <main> and <footer> structure the page for everyone.
  • Headings form the outline: one <h1>, logical nesting, no skipped levels, size set in CSS.
  • Links navigate, buttons act; give images meaningful alt text or alt="" if decorative.
  • Form fields need a name to submit and a <label> to be usable; built-in validation helps users but never replaces server checks.

Check yourself

Quiz

0/4 correct · 4 left

  1. 1.A form has <input id="city" type="text" required> and submits successfully, but the server never receives the city. Why?

  2. 2.Which heading structure is correct for a page about tea with two subtopics?

  3. 3.A "Show password" <button> inside a form submits the form when clicked. What fixes it?

  4. 4.An image is purely decorative. What should its alt attribute be?

Concepts in this lesson

One-page notes on each idea, for review or a quick lookup later.

Finished reading?

Mark it complete to fill in your roadmap.