web · Level 2

HTML: build a meaningful, accessible page

Write a real web page by hand, check it with the W3C validator and your keyboard, and learn to review the HTML an AI assistant gives you.

By Mickarle Wagstaff-Irons - Micky Irons

  • Level 2Getting going
  • 130 min
  • 6 chapters
  • Online course and PDF workbook
The Pageweb / 02

Read, practise and review

Your next step

Start this course online

Register your start, then work through the lessons and exercises at your own pace. You can download the PDF separately. Where a scored exam is available, you will find it after the lessons.

Register or resume your course

Mickai privately records this registration and sends its team a course-start notification. It records your intention to start, not proof of attendance or completion. Our course-start privacy notice explains the details and the 30-day resume cookie.

Follow the Mickai company page on LinkedIn

You can start learning without subscribing or following. Newsletter email confirmation and provider enrolment are recorded separately.

Start with the essentials

The short answer

Write HTML that says what things are: a page title, headings in order, landmarks such as header, nav, main and footer, lists, labelled form controls, real buttons and links, and meaningful alt text. Browsers, screen readers and search engines read that structure. Check it with the W3C Nu Html Checker, a keyboard-only pass and a contrast test, knowing that tools alone cannot judge accessibility.

What you will learn

  • You will be able to write a valid HTML page with a language, title, headings, landmarks, lists, links, an image, a data table and a labelled form.
  • You will be able to choose the right element for the job, and explain why a div is not a heading, a list or a button.
  • You will be able to check a page with the W3C Nu Html Checker, read its messages and say what it cannot see.
  • You will be able to test a page with the keyboard alone and with a colour contrast calculation.
  • You will be able to say what WCAG 2.2 is, which of its criteria this course exercises, and why these checks do not make a page "WCAG compliant".
  • You will be able to review HTML that an AI assistant generated and find its common faults.

Who it is for

Beginners to web pages, comfortable with a computer, a text editor and a browser, who want to write a real page by hand and understand what an AI assistant produces when it is asked for one. No JavaScript and no frameworks are used, and CSS only lightly.

Before you start

  • None. You need a computer, a plain text editor and a web browser.

Learn in your browser · Chapter 01 of 06

01

Meaning first: the smallest valid page

HTML says what each part of a page is. Write the smallest valid page, then meet the checker.

HTML is text with tags. A tag such as <h1> says what its content is: a main heading, a list, a link. It does not say how the content looks: that is CSS. Browsers turn HTML into the page you see and into an accessibility tree, which holds each item's name, role and state for screen readers.

You need a computer, a plain text editor and a browser. Make a folder called event-page, save each file there as plain text (UTF-8) with the ending shown, and double-click a page to open it in your browser. Only what you paste into the checker goes online. Save the smallest valid page as minimal.html.

html · 11 lines
<!doctype html>
<html lang="en-GB">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Marigold Lane Seed Swap</title>
</head>
<body>
  <h1>Marigold Lane Seed Swap</h1>
</body>
</html>

Scroll sideways to see every column.

Meaning first: the smallest valid page · Table 1
LineWhat it does
<!doctype html>Standards mode, not "quirks mode".
lang="en-GB"The page's language, so speech software pronounces words correctly.
<meta charset="utf-8">UTF-8, the only valid encoding, so accents and symbols display.
<meta name="viewport" ...>Layout width matches the device's screen width.
<title>Names the page in tabs, bookmarks and search results. Screen readers read it first.

Check it. Open validator.w3.org/nu, choose text input beside "Check by", paste in the page and press Check. The W3C Nu Html Checker reads HTML as a browser does and lists mistakes. For minimal.html it says Document checking completed. No errors or warnings to show. Here is a broken page, broken.html, and the reply (positions, extracts and guidance links left out).

html · 13 lines
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div class="top">Marigold Lane Seed Swap</div>
<h4>Programme</h4>
<img src="packets.svg">
<label for="nm">Name</label>
<input id="name" type="text">
<p>To join in, <a href="#name">click here</a>.</p>
</body>
</html>

text · 6 lines
Warning: Consider adding a lang attribute to the html start tag to declare the language of this document.
Error: Start tag seen without seeing a doctype first. Expected <!DOCTYPE html>.
Error: Element head is missing a required instance of child element title.
Error: An img element must have an alt attribute, except under certain conditions. For details, consult guidance on providing text alternatives for images.
Error: The value of the for attribute of the label element must be the ID of a non-hidden form control.
Warning: This document has heading elements but none of them has a computed heading level of 1.

Error means a rule is broken and Warning is advice. Work from the top, because one early mistake can cause later messages. The checker said nothing about the div standing in for a heading or the words "click here": both are valid, neither is meaningful. Its own page says it is a checker, not a pass or fail certificate, and that its checks change over time.

Try it yourself · Activity 01

15 min

Fix the broken page

Type broken.html exactly as shown and repair it, first until the checker is silent and then for meaning.

  1. Check the page and compare the reply with the list above. Fix from the top, checking after each fix.
  2. Then fix what the checker cannot see: the event name should be a real h1, Programme an h2 beneath it, and the link text should say where it goes.

Which fix needed a decision from you rather than a mechanical change?

Worked answer

Checker fixes: <!doctype html> on the first line; lang="en-GB" on <html>; <title>Marigold Lane Seed Swap</title> in the head; an alt on the image, such as alt="Two seed packets"; and for="name" to match the input's id. Structure fixes: the top div becomes <h1>, which answers the heading warning; the h4 becomes <h2>, or the checker reports a skipped heading level; and click here becomes Sign up for the swap. The checker then reports No errors or warnings to show. The decision was the heading: the checker only knows that no level 1 exists, and you choose the main heading.

Next chapter

Learn in your browser · Chapter 02 of 06

02

Build the event page

Type in a small, real page for an invented community event, then see why each part is there.

The event, place and words are invented. Save this as event-page/index.html, typing it rather than pasting so you meet every line.

html · 85 lines
<!doctype html>
<html lang="en-GB">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Marigold Lane Seed Swap: Saturday 17 April 2027</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <header>
    <p>Marigold Lane Seed Swap</p>
    <nav aria-label="On this page">
      <ul>
        <li><a href="#about">About the day</a></li>
        <li><a href="#programme">Programme</a></li>
        <li><a href="#signup">Sign up</a></li>
      </ul>
    </nav>
  </header>

  <main>
    <h1>Marigold Lane Seed Swap</h1>
    <p>Bring spare seeds and cuttings. Take home something new.</p>
    <svg viewBox="0 0 320 160" role="img" aria-labelledby="art-title">
      <title id="art-title">A seed packet and a small sprouting plant on a table</title>
      <rect width="320" height="160" fill="#efe6cf"/>
      <rect x="60" y="112" width="200" height="10" fill="#1b1a17"/>
      <rect x="100" y="60" width="44" height="52" fill="#fbf7ec" stroke="#1b1a17" stroke-width="3"/>
      <path d="M212 112V84M212 96Q196 94 192 80Q208 80 212 96M212 90Q228 88 232 74Q216 74 212 90" fill="none" stroke="#1b1a17" stroke-width="3"/>
    </svg>

    <section id="about">
      <h2>About the day</h2>
      <p><time datetime="2027-04-17">Saturday 17 April 2027</time>, 10:00 to 15:00, at Marigold Lane Village Hall (an invented place). Free.</p>
      <h3>What to bring</h3>
      <ul>
        <li>Seeds in labelled envelopes</li>
        <li>Cuttings wrapped in damp paper</li>
        <li>A bag for what you take home</li>
      </ul>
    </section>

    <section id="programme">
      <h2>Programme</h2>
      <table>
        <caption>Programme for Saturday 17 April 2027</caption>
        <thead>
          <tr><th scope="col">Time</th><th scope="col">Activity</th></tr>
        </thead>
        <tbody>
          <tr><th scope="row">10:00</th><td>Doors open and swap tables set up</td></tr>
          <tr><th scope="row">11:30</th><td>Swap tables open</td></tr>
          <tr><th scope="row">13:30</th><td>Children's seed-planting workshop</td></tr>
        </tbody>
      </table>
    </section>

    <section id="signup">
      <h2>Sign up</h2>
      <p>This form is a demonstration. Nothing is behind it, so it sends nothing anywhere. Type invented details only.</p>
      <form>
        <p>
          <label for="name">Your name</label>
          <input id="name" name="name" type="text">
        </p>
        <p>
          <label for="email">Email address</label>
          <input id="email" name="email" type="email" aria-describedby="email-hint">
        </p>
        <p id="email-hint">An invented address is fine. Nothing is stored.</p>
        <fieldset>
          <legend>Will you bring seeds to swap?</legend>
          <p><label><input type="radio" name="bring" value="yes"> Yes</label></p>
          <p><label><input type="radio" name="bring" value="no"> No, I am coming to browse</label></p>
        </fieldset>
        <p><button type="button">Join the list (demonstration only)</button></p>
      </form>
    </section>
  </main>

  <footer>
    <p>An invented event and place, made for learning.</p>
  </footer>
</body>
</html>

Save this beside it as style.css. It only makes the page readable.

css · 25 lines
body {
  margin: 0;
  font-family: system-ui, sans-serif;
  font-size: 1.125rem;
  line-height: 1.6;
  color: #1b1a17;
  background: #fbf7ec;
}
header, main, footer {
  max-width: 42rem;
  margin: 0 auto;
  padding: 0.5rem 1rem;
}
a { color: #6b4f00; }
:focus-visible {
  outline: 3px solid #1b1a17;
  outline-offset: 3px;
}
nav ul { display: flex; flex-wrap: wrap; gap: 0.5rem; padding: 0; list-style: none; }
nav a { display: inline-block; padding: 0.5rem 0.75rem; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 0.5rem; border: 1px solid #1b1a17; text-align: left; }
label { display: block; font-weight: 600; }
input[type="text"], input[type="email"], button { min-height: 2.75rem; font: inherit; }
input[type="radio"] { width: 1.5rem; height: 1.5rem; margin-right: 0.5rem; vertical-align: middle; }

To use your own picture later, replace the svg element with <img src="my-picture.jpg" alt="Describe the picture here">.

Scroll sideways to see every column.

Build the event page · Table 2
PartElementsJob
Menuheader, navBanner and navigation regions. aria-label names the menu.
Page contentmainThe dominant content. A page has one visible main.
Outlineh1, h2, h3One h1, h2 sections, an h3 inside one.
What to bringul, liItems that belong together.
Programmetable, caption, th scopeData with named row and column headers.
Formlabel, fieldset, legend, buttonEvery control has a name (chapter 4).
FooterfooterClosing information, in its own region.

This is part of the page's accessibility tree, as a test tool read it from Edge, with lines left out (dots). To see your own, right-click an element in Edge, choose Inspect, then open the Accessibility tab beside Styles.

text · 21 lines
- banner:
  - navigation "On this page":
    ...
        - link "About the day":
    ...
- main:
  - heading "Marigold Lane Seed Swap" [level=1]
  - img "A seed packet and a small sprouting plant on a table"
  - heading "About the day" [level=2]
    ...
  - heading "Programme" [level=2]
  - table "Programme for Saturday 17 April 2027":
    ...
  - heading "Sign up" [level=2]
    ...
    - textbox "Your name"
    ...
  - group "Will you bring seeds to swap?":
    ...
    - button "Join the list (demonstration only)"
- contentinfo:

Every item has a name and a role. People using screen readers can jump between headings and landmarks instead of hearing the whole page, and the W3C notes that keyboard users and reader modes benefit too. MDN says search engines weigh headings and links above text in plain divs, which promises nothing about your ranking. So choose elements by meaning: MDN says to use the generic div only when nothing more meaningful fits. MDN also says not to pick a heading level for its size: nest levels without skipping any, then size them in CSS.

Try it yourself · Activity 02

35 min

Build it and open it

Type both files, open the page and check them.

  1. Save index.html and style.css in event-page and open index.html.
  2. Read the tab's title. Check the HTML (text input) and the CSS (file upload).
  3. Click each menu link and notice where the page moves.

What would a screen reader user meet first?

Worked answer

You should see a cream page with a menu of three links, a heading, a simple drawing, a list, a three-row table and a form. The tab reads Marigold Lane Seed Swap: Saturday 17 April 2027, and the checker reports No errors or warnings to show for both files. If not, go to the line the checker names: usual causes are a missing closing tag, a mistyped attribute or a for and id that do not match. A plain white page means style.css is not beside index.html or is misnamed. A screen reader user meets the banner and its navigation first, then the main content, in the order of the source.

Next chapter

Learn in your browser · Chapter 03 of 06

03

Links, images and tables

Vague links, useless alt text and fake tables are three easy faults to make.

Links. A link's text should say where it goes, even read alone, because people using screen readers can pull up a list of a page's links. "See the programme" works. "Click here" does not.

Images. alt is text that stands in for the image. The HTML standard's test: replacing every image with its alt text should not change the page's meaning. Choose by purpose, as the W3C's decision tree does.

  • Informative: a short replacement for what it says, such as alt="Queue at the swap table".
  • Decorative: an empty alt="", so software skips it.
  • Functional (inside a link or button): what it does, such as alt="Print the programme", not "printer icon".
  • Text in an image: the same words. For a chart, keep the alt short and put the full data in the page.

Never write "image of" or a file name (MDN). The checker reports a missing alt, but not a useless one. An svg has no alt: the event page names its drawing with role="img" and a <title>, and Edge listed it as an image with that name.

Tables hold data in rows and columns, never layout. Mark header cells th, say which way they point with scope="col" or scope="row", and add a caption as the title. Screen readers can then read each cell with its headers. A grid of divs offers none of that.

Try it yourself · Activity 03

10 min

Write three alt texts

Three invented images are needed on the event page. Decide what each is for, then write its alt.

  1. Image 1 sits under About the day: a photo of the swap table, with labelled seed envelopes, a tray of seedlings and a handwritten sign that reads "Take one, leave one".
  2. Image 2 is a thin gold flourish between two sections. It adds nothing to the meaning.
  3. Image 3 is a small printer icon, the only content of a link to a printable programme.
  4. Name each type and write the img tag with its alt. Read the three alts aloud with the images imagined away.

Which image was hardest to classify, and what would change your answer?

Worked answer

1 Informative: alt="Swap table with labelled seed envelopes, a tray of seedlings and a handwritten sign: Take one, leave one." The sign's words matter, so they are included. 2 Decorative: alt="". 3 Functional: alt="Print the programme", which names what the link does, not the icon. Avoid "image", "photo.jpg" and "printer icon". In the test run all three passed the checker, and so did alt="image". Any wording that keeps the same meaning is fine.

Next chapter

Learn in your browser · Chapter 04 of 06

04

Forms and buttons

A form is where people give you something, so every control must say what it is for.

Labels. Give every field a visible label joined to it: the for attribute must match the field's id. Screen readers then announce the name, and clicking the label, a bigger target, puts the cursor in the field (W3C, MDN). Wrapping the field in its label, as the radio buttons do, also works, but the W3C says for and id is generally better supported. Put related options in a fieldset with a legend. Tie help text, like the note under the email field, to its field with aria-describedby. Do not use placeholder as a label: MDN advises avoiding it, because it disappears when someone types.

Links go places, buttons do things. Use a href to move to another page or place, and button to act on the current page. A plain div made to look like a button cannot be reached with Tab and lacks a real button's keyboard behaviour (MDN). Give a button type="button" unless it should submit a form.

This form passes the checker and still fails people. Save minimal.html as form.html and replace its <h1> line with it.

html · 10 lines
<form>
<div>Your name</div>
<input type="text" name="name">
<label>Email address</label>
<input type="email" name="email">
<input type="text" name="group" placeholder="People in your group">
<div>Bringing seeds?</div>
<input type="radio" name="bring"> Yes
<input type="radio" name="bring"> No
</form>

Try it yourself · Activity 04

10 min

Label a form correctly

Repair the form so every control has a proper name and related options are grouped.

  1. Check the form. Then, in the browser, click the words Your name and the word Yes. Note what happens.
  2. Give each text field a label joined by for and id, including the field that has only a placeholder.
  3. Group the radio buttons in a fieldset with a legend, and wrap each button and its word in a label.
  4. Check again and repeat the clicks.

Why did the checker say nothing about the original form?

Worked answer

Both versions passed the checker: it tests that markup is allowed, not that controls are named. In the original, clicking the words did nothing, and Edge's tree showed two unnamed text boxes, a third named only by its placeholder, and two unnamed radio buttons. A repaired field is <p><label for="name">Your name</label> <input id="name" name="name" type="text"></p>, and the email and group fields follow the same pattern. The radio buttons become <fieldset><legend>Bringing seeds?</legend><p><label><input type="radio" name="bring"> Yes</label></p>, a second label for No, then </fieldset>. Now clicking a label focuses its field or selects its radio button, and the tree names every control and the group Bringing seeds?

Next chapter

Learn in your browser · Chapter 05 of 06

05

Test it like a user

Tools find some problems. Your own hands and eyes find others.

Keyboard test. Put the mouse aside. Tab moves to the next link or control, Shift and Tab moves back, Enter follows a link or presses a button, Space presses a button or ticks a box, and the arrow keys move within a group of radio buttons. Look for every control reachable, an order that follows the reading order, and a focus outline you can always see. In the test run the event page's Tab order was:

text · 8 lines
Tab 1  link "About the day"
Tab 2  link "Programme"
Tab 3  link "Sign up"
Tab 4  textbox "Your name"
Tab 5  textbox "Email address"
Tab 6  radio "Yes"
Tab 7  button "Join the list (demonstration only)"
Tab 8  focus leaves the page content

Every stop had a solid 3-pixel outline. The two radio buttons are one stop, and the down arrow moved the choice from Yes to No. Pressing Enter in a text field, or pressing the button, did nothing: the HTML standard says a form with no submit button and more than one text field does not submit on Enter.

Colour contrast. WCAG asks for a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text (18 point, or 14 point bold). The ratio is (L1 + 0.05) / (L2 + 0.05), where L1 and L2 are the relative luminance, from 0 for black to 1 for white, of the lighter and darker colour. Tools do the sums, and the W3C suggests looking for one in its evaluation tools list. On the event page the text colour on cream is 16.26:1 and the link colour on cream 7.15:1. Grey #999999 on white is 2.85:1 and fails, #777777 on white is 4.48:1 and just fails, and #767676 on white is 4.54:1 and just passes.

Text size. Sizes set in rem, as in this CSS, follow the reader's browser settings (MDN). WCAG asks that text can be resized to 200% without losing content or function, so zoom your browser (Ctrl or Cmd and +) to 200% and look for lost text and sideways scrolling. Its reflow criterion asks for no sideways scrolling at 320 CSS pixels wide, which the page met in the test run. Doubling the root text size at that width as well, a stricter test than WCAG sets, made the two text fields grow until the page was 456 pixels wide.

Target size and the limits of tools. WCAG 2.2 asks for pointer targets of at least 24 by 24 CSS pixels, with exceptions such as links inside a sentence. Measured here: menu links 45 pixels tall, text fields 50, the button 44, radio buttons 24 by 24. The W3C says evaluation tools cannot determine accessibility, they can only assist, and GOV.UK asks for automated and manual testing together because automated testing alone misses some issues.

Try it yourself · Activity 05

15 min

Keyboard and contrast test

Test your own event page without the mouse, then check one colour pair.

  1. Reload index.html and press Tab. Browser controls may take a few presses, so count only stops inside the page. Write each stop down until focus leaves the page.
  2. At each stop check that the focus outline is visible.
  3. At the radio buttons press the down arrow. In the name field press Enter, then press the button. What happens?
  4. Work out the contrast ratio of #999999 text on white. The relative luminance of #999999 is 0.3185, and white's is 1.

What would you have missed by running only the checker?

Worked answer

Your list should match the eight lines above if you typed the page as shown. The outline is thick and dark at every stop. The down arrow moves the choice from Yes to No. Enter in the field and the button do nothing, because nothing is behind the form. Contrast: (1 + 0.05) / (0.3185 + 0.05) = 1.05 / 0.3685 = 2.85, below 4.5, so grey #999999 body text on white fails. The checker reports none of the keyboard findings.

Next chapter

Learn in your browser · Chapter 06 of 06

06

WCAG 2.2, and reviewing what an assistant writes

Put a name to what you have been checking, then use it on HTML you did not write.

WCAG (Web Content Accessibility Guidelines) is the W3C's standard for accessible web content. WCAG 2.2 is a W3C Recommendation, published on 5 October 2023 and updated on 12 December 2024. Its testable success criteria sit under four principles (perceivable, operable, understandable, robust) at levels A, AA and AAA, and it adds nine criteria to WCAG 2.1. It also removed 4.1.1 Parsing, so valid HTML is good practice rather than a criterion of its own. This course exercises these criteria.

Scroll sideways to see every column.

WCAG 2.2, and reviewing what an assistant writes · Table 3
CriterionLevelIn short
1.1.1 Non-text ContentANon-text content has a text alternative.
1.3.1 Info and RelationshipsAStructure shown visually is in the markup.
1.4.3 Contrast (Minimum)AA4.5:1 for normal text, 3:1 for large.
1.4.4 Resize TextAAText resizes to 200% without loss.
2.1.1 KeyboardAAll functions work from a keyboard.
2.4.2 Page TitledAPages have descriptive titles.
2.4.4 Link Purpose (In Context)AA link's purpose can be worked out.
2.4.7 Focus VisibleAAKeyboard focus is visible.
2.5.8 Target Size (Minimum)AA, new in 2.2Pointer targets are at least 24 by 24 CSS pixels, with exceptions.
3.1.1 Language of PageAThe page language can be determined.
3.3.2 Labels or InstructionsAInput has labels or instructions.
4.1.2 Name, Role, ValueAControls expose a name and role to software.

Reviewing an assistant's HTML. An assistant can write a page in seconds. What it writes can be plausible and valid, and still div soup, unlabelled or vague. Treat it as a draft from a stranger: run the checker, read the tree, press Tab, and ask. Is there a main and one h1, with headings in order? Does every image have a meaningful alt, every field a label? Are buttons real buttons? Can everything be done from the keyboard? Never paste passwords, keys or other people's data into a prompt.

This example was written for this workbook to show the kind of output an assistant can give. It is not the output of any particular product. Save minimal.html as review.html and add this line just before </head>.

html · 1 line
<style>a:focus, .btn:focus { outline: none; }</style>

Then replace the <h1> line with these lines.

html · 10 lines
<div class="page">
<div class="header"><h3>Community Garden Open Day</h3></div>
<img src="garden.jpg" alt="image">
<h1 class="small">What's on</h1>
<div class="row"><b>10:00</b> Planting <b>12:00</b> Tea and cake</div>
<span style="color:#999999">Free entry, all welcome</span>
<a href="/map">Click here</a> for the map.
<input type="text" placeholder="Your email">
<div class="btn" onclick="signUp()">Sign up</div>
</div>

Try it yourself · Activity 06

15 min

Review the snippet

Review it as you would a page from a stranger. List at least six problems and write a fix for each.

  1. Build review.html, run the checker and press Tab through the page.
  2. Note every problem you can find, whether or not the checker reports it, and write a one-line fix for each.

Which problem would be hardest to spot if the assistant had explained its code confidently?

Worked answer

The checker reported no errors or warnings. Tab reached two stops (the link, then the text box) and never the Sign up div, and the a:focus rule had removed the link's outline. Problems and fixes: (1) only divs: use header, main, footer; (2) headings picked for size: one h1 first, then h2, sized by CSS; (3) alt="image": describe the picture, or use alt="" if it adds nothing; (4) times in bold text: use a list or table; (5) #999999 on white is 2.85:1: use 4.5:1 or more; (6) "Click here": say what the link does, such as "See the map"; (7) placeholder as the only label: add a label; (8) a div as a button: use <button type="button">; (9) removed focus outline: delete the rule. Any six of these, or others you can justify, are enough.

Assessment options

Keep learning

The complete workbook

This workbook teaches HTML as structure and meaning, not looks. You will build a small invented community event page with a heading outline, navigation, an image, a programme table and a sign-up form that sends nothing anywhere, then check it with the W3C Nu Html Checker, your keyboard and a colour contrast calculation. You will also learn what WCAG 2.2 is and how to review HTML written by an AI assistant.

  1. 01
    Meaning first: the smallest valid page

    HTML says what each part of a page is. Write the smallest valid page, then meet the checker.

    Read here · 1 exercise
  2. 02
    Build the event page

    Type in a small, real page for an invented community event, then see why each part is there.

    Read here · 1 exercise
  3. 03
    Links, images and tables

    Vague links, useless alt text and fake tables are three easy faults to make.

    Read here · 1 exercise
  4. 04
    Forms and buttons

    A form is where people give you something, so every control must say what it is for.

    Read here · 1 exercise
  5. 05
    Test it like a user

    Tools find some problems. Your own hands and eyes find others.

    Read here · 1 exercise
  6. 06
    WCAG 2.2, and reviewing what an assistant writes

    Put a name to what you have been checking, then use it on HTML you did not write.

    Read here · 1 exercise

Also inside: a 10-point checklist, a glossary of 11 terms and 10 questions and answers to test yourself. 6 hands-on exercises, each with a worked answer at the back where the workbook gives one.

The PDF is free. We ask for your name and email before downloading. Read the privacy notice at the download form. Free to download and use for personal learning, study groups and inside your own team. Please do not resell the workbooks or republish them as your own. Link people to trust-agent.ai instead.

Get your free PDF workbook

Enter your name and email, then download the complete workbook. This form also works without JavaScript.

We privately record your name, email and request IP for this download. Read our privacy notice and retention policy.

Visit Mickai on LinkedIn

Newsletter confirmation does not affect your download. Following Mickai on LinkedIn is optional; this site does not verify followers.

Test yourself

Questions and answers

What is semantic HTML?

HTML that uses the element matching the meaning of the content: h1 for a main heading, nav for a menu, button for a button, table for data. Browsers, screen readers and search engines can then tell what each part is. A div or span says nothing, so use one only when no meaningful element fits.

Does valid HTML mean an accessible page?

No. The Nu Html Checker tests whether markup follows the rules, not whether it is meaningful or usable. In this workbook's tests a snippet built mostly from divs, a form with no labels and an image with a useless alt text all passed with no messages. Use the checker to catch mistakes, then test with a keyboard and read the accessibility tree.

When should alt be empty?

When the image adds nothing the page does not already say, such as a decorative flourish, or an icon beside text that says the same. Write alt="", with the attribute present, so software skips it. Never leave the attribute out, and never use an empty alt for an image that carries information or is the only content of a link.

What should link text say?

Where the link goes or what it does, in words that make sense when read alone, such as "See the programme". Avoid "click here": people using screen readers can pull up a list of a page's links, and a list of identical vague phrases tells them nothing.

Should I use a link or a button?

Use a link (a with href) to go to another page or another place on the page. Use a button to do something on the current page. Real links and buttons can be reached with Tab and operated from the keyboard without extra work, and a div styled to look like one cannot. Add type="button" unless the button should submit a form.

Why does my form do nothing?

A form only gathers what someone types. To receive it, a program on a web server must be listening at the address the form sends to, and this workbook does not build one. The practice form's button is type="button" for that reason. Do not put a form on a public page unless you know where its data goes, and never test with real personal details.

How do I test a page with only a keyboard?

Put the mouse aside. Press Tab to move through links and controls, Shift and Tab to go back, Enter for links and buttons, Space for buttons and tick boxes, and the arrow keys inside radio groups. Check that everything is reachable, that the order follows the reading order and that you can always see where the focus is.

How much contrast does text need?

WCAG 2.2 level AA asks for a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text (18 point, or 14 point bold). The ratio runs from 1:1 to 21:1 and is worked out from the brightness of the two colours. Use a contrast tool: grey #777777 on white is 4.48:1, so it just fails.

What is WCAG 2.2, and can I call my page compliant?

WCAG 2.2 is the W3C's web accessibility standard, a W3C Recommendation since 2023. Conformance means meeting every relevant criterion at a chosen level for whole pages, judged by people as well as tools. The checks in this workbook cover only some criteria, so say what you tested instead of claiming compliance. UK public sector bodies should read GOV.UK's guidance.

Can I trust HTML written by an AI assistant?

Treat it as a draft. It can be valid and plausible while still using divs for everything, missing labels, giving vague alt text or choosing headings for their size. Run the checker, read the accessibility tree, press Tab through the page and check contrast before you use it. Never paste passwords, keys or other people's data into a prompt.

Write as you go

My notes

Use this scratchpad for your own notes, working or answers as you read. Notes are saved in this browser on this device. Downloading makes a local copy; it does not send your notes to Mickai.

Consolidate your learning

Review your work

Work through the exercises and questions, then compare your reasoning with the worked answers. A scored exam and verifiable certificate are not yet available for this course.

Review the questions

Learn the language

Key terms

HTML
HyperText Markup Language: text with tags that says what each part of a page is.
Element and tag
A tag such as <p> marks the start or end of an element, one piece of a page with a meaning.
Semantic element
An element whose name says what it is, such as nav, main, h1 or table, as opposed to the generic div and span.
Landmark
A region of a page, such as banner, navigation, main or contentinfo, that assistive technology can jump to. header, nav, main and footer create them.
Accessibility tree
The browser's model of a page's items, with a name, role and state for each. Screen readers and other assistive technology use it.
Alt text
The alt attribute: text that replaces an image for people and software that cannot see it. An empty alt="" marks an image that adds nothing.

6 of the workbook's 11 terms. The complete glossary is in the workbook.

Follow the evidence

Sources and checks

Facts last checked: .

Examples in this workbook were run on: Microsoft Edge 153.0.4234.48 (system install) driven by Playwright 1.59.0 for Python 3.12.10 on Windows 11 Pro; HTML and CSS checked with the W3C Nu Html Checker online service (validator.w3.org/nu, checker version 26.9.16) through its JSON API and the form submission its web page uses (2026-09-26).

These workbooks use AI assistance. See how the workbooks are made.

  1. Web Content Accessibility Guidelines (WCAG) 2.2W3C
  2. WCAG 2 OverviewW3C Web Accessibility Initiative
  3. What's New in WCAG 2.2W3C Web Accessibility Initiative
  4. Page Structure TutorialW3C Web Accessibility Initiative
  5. Page RegionsW3C Web Accessibility Initiative
  6. HeadingsW3C Web Accessibility Initiative
  7. Images TutorialW3C Web Accessibility Initiative
  8. An alt Decision TreeW3C Web Accessibility Initiative
  9. Tables TutorialW3C Web Accessibility Initiative
  10. Caption & SummaryW3C Web Accessibility Initiative
  11. Forms TutorialW3C Web Accessibility Initiative
  12. Labeling ControlsW3C Web Accessibility Initiative
  13. Grouping ControlsW3C Web Accessibility Initiative
  14. Easy Checks: A First Review of Web AccessibilityW3C Web Accessibility Initiative
  15. Page Title (Easy Checks)W3C Web Accessibility Initiative
  16. Language (Easy Checks)W3C Web Accessibility Initiative
  17. Visible Keyboard Focus (Easy Checks)W3C Web Accessibility Initiative
  18. Color Contrast (Easy Checks)W3C Web Accessibility Initiative
  19. Zoom Levels (Easy Checks)W3C Web Accessibility Initiative
  20. Selecting Web Accessibility Evaluation ToolsW3C Web Accessibility Initiative
  21. Nu Html Checker (web service)W3C
  22. About the Nu Html CheckerW3C
  23. The Nu Html Checker (vnu)Nu Html Checker project
  24. Service: Input: POST body (Nu Html Checker wiki)Nu Html Checker project, GitHub
  25. HTML: A good basis for accessibilityMDN Web Docs
  26. Accessibility treeMDN Web Docs
  27. DoctypeMDN Web Docs
  28. The Metadata element: metaMDN Web Docs
  29. The meta name="viewport" attribute valueMDN Web Docs
  30. The Image Embed element: imgMDN Web Docs
  31. The Form Label element: labelMDN Web Docs
  32. The HTML Input element: inputMDN Web Docs
  33. The Button element: buttonMDN Web Docs
  34. CSS length (including the rem unit)MDN Web Docs
  35. The Content Division element: divMDN Web Docs
  36. HTML section heading elements (h1 to h6)MDN Web Docs
  37. HTML Standard: Grouping content (the main element)WHATWG
  38. HTML Standard: Document metadata (the title element and character encoding)WHATWG
  39. HTML Standard: Images (text alternatives)WHATWG
  40. HTML Standard: Form control infrastructure (implicit submission)WHATWG
  41. Understanding accessibility requirements for public sector bodiesGOV.UK, Government Digital Service and Central Digital and Data Office
  42. Testing for accessibilityGOV.UK Service Manual
  43. Test accessibility using the Accessibility tabMicrosoft Learn, Microsoft Edge developer documentation

Created by Mickarle Wagstaff-Irons - Micky Irons with the Mickai team. Published by Mickai LTD. Last updated 26 September 2026.

NextKeep going

Where to go next