0 / 91
Week 2 · Day 10 of 91

Forms and accessible labels

HTML, CSS, Git, and the First Website

Objective

Build a usable form before adding any backend.

HTML is the mechanical structure, CSS is the physical layout and finish, and Git is the revision history for every design change.

  • labels and controls
  • input types
  • native validation

Why this matters

A form is where a website stops being a document and starts being a machine — the visitor puts data in. You have no backend yet, so nothing is stored today. That is deliberate: a badly built form cannot be rescued by the server behind it, so you build the usable part first.

By the end you will have a contact form that someone who never touches a mouse can complete, and you will be able to prove it rather than assume it.

The controls

A control is an element the visitor operates. Four cover almost everything: <input> for single-line values, <textarea> for multi-line text, <select> with <option> for a fixed list of choices, and <button> for doing something. They live inside a <form>:

<form action="/contact" method="post">
  <!-- controls go here -->
  <button type="submit">Send message</button>
</form>

action is the URL the data is sent to; method is how (get puts values in the URL, post puts them in the request body). With no server to receive it, leave action off for now — a form without one submits back to the page it is on. Week 6 fills this in.

Each control needs a name: the key its value is sent under. A control without one is not submitted at all. <button type="submit"> sends the form.

Labels, and the for/id pair

Every control needs a visible label connected to it in markup. Text sitting next to a box is not a connection.

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

The connection is made when the label's for value matches the input's id. Those two strings must be identical, and the id must be unique on the page. That is the whole mechanism, and it is the single most-missed thing in beginner HTML.

What the connection buys you:

  • A screen reader announces the label when focus reaches the control. Without it, the user hears "edit text, blank".
  • Clicking the label focuses the control — a free test. If the cursor jumps into the box, the pair is wired; if nothing happens, for and id do not match.
  • The click target gets bigger, which helps anyone with a tremor or a small screen.

`for`/`id` is the designator on the silkscreen

A silkscreen "R7" is useful only because exactly one component in the BOM is also called R7. The printed text and the part are bound by a shared unique identifier, and a board with two parts marked R7 is unserviceable. for and id are that binding — duplicate an id and the browser binds the label to the first match, with the same ambiguity and the same result.

The name tag beside the doorbell

A block of flats has a row of identical buttons and a name card beside each. The button works without the card, but only for someone who already knows which is which.

A placeholder is not a label

<input placeholder="Email"> puts grey hint text inside the box. It vanishes the moment the user types, so anyone interrupted loses the only clue about the field; its contrast is usually too low; and some screen readers ignore it. Use a real <label>. A placeholder may sit alongside one as an example of the format.

Input types

<input> changes behaviour entirely based on its type:

type Gives you
text A plain single-line box (the default)
email Email keyboard on phones, plus format checking
tel Numeric keypad on phones
number A numeric field accepting min, max, step
date The browser's own date picker
checkbox An independent on/off toggle
radio One choice from a group sharing a name
password Characters masked as you type

The type is a usability decision, not a cosmetic one: type="email" on a phone produces a keyboard with @ on it, and type="text" does not.

Native validation

The browser can check some things before submission, with no code from you:

  • required — the field cannot be left empty.
  • type="email" or type="url" — the value must look like one.
  • minlength / maxlength — length limits on text.
  • min / max — value limits on numbers and dates.
  • pattern="[0-9]{4}" — the value must match that pattern.

If a rule fails, the browser blocks submission, focuses the offending field, and shows a small message near it. That wording varies by browser and language, so never write page text quoting it.

Browser validation is convenience, never security

Every check above runs on the visitor's machine, and the visitor controls that machine — DevTools deletes a required attribute in two seconds, and a request can be sent without your form at all. This is the Day 1 rule: rules enforced only in the browser are suggestions. You will validate all of it again on the server in Week 6.

Walkthrough: a contact form

Create contact.html in profile-site with the same <head> and <nav> as your other pages, and this inside <main>:

<section>
  <h2>Contact me</h2>
  <form>
    <p>
      <label for="name">Your name</label><br />
      <input type="text" id="name" name="name" required autocomplete="name" />
    </p>
    <p>
      <label for="email">Email address</label><br />
      <input type="email" id="email" name="email" required autocomplete="email" />
    </p>
    <p>
      <label for="topic">Topic</label><br />
      <select id="topic" name="topic">
        <option value="work">Work enquiry</option>
        <option value="project">Question about a project</option>
        <option value="other">Something else</option>
      </select>
    </p>
    <p>
      <label for="message">Message</label><br />
      <textarea id="message" name="message" rows="6" minlength="10" required></textarea>
    </p>
    <button type="submit">Send message</button>
  </form>
</section>

Open it and try three things. Click the words "Email address" — the cursor jumps into the box, proving the pair is connected. Submit with everything empty — the browser refuses and focuses the first empty required field. Type hello into the email field and submit — it refuses again, because that is not email-shaped.

Add contact.html to the <nav> list on every page, as on Day 9.

Do the whole form with no mouse

Click once in the address bar, then press Tab repeatedly. Focus moves through every control in order with a visible ring; Shift+Tab goes back; arrow keys change the <select>. Reach and set every control this way and the form is keyboard-usable. On macOS Safari, if Tab skips the select and button, enable Settings → Advanced → "Press Tab to highlight each item on a webpage" — a browser setting, not a bug in your page.

Your turn

  1. Build contact.html as above, typing rather than pasting, so the for/id pairs go through your fingers.
  2. Add a fifth control with a suitable type — tel for a phone number, or date. Give it a label, an id, and a name.
  3. Confirm every control has a <label> whose for matches its id, by clicking each label and watching focus move.
  4. Do the keyboard pass from the [!try] block above and note the tab order you observe.
  5. Submit with valid data. The page reloads with the values in the URL as ?name=...&email=... — proof the data left the controls, and a preview of Week 6.
  6. In DevTools → Elements, select an input and open the Accessibility pane. Its computed name should be your label text; blank means the pairing is broken.

You are done when

Every control has a label whose for matches its id, and you completed the entire form — reaching and setting every control, then submitting — without touching the mouse once.

Reviewer mode, once your form works

Today's AI mode is skeptical reviewer: you hand over finished work and ask for concrete defects, not encouragement. A good review produces specific, actionable findings backed by evidence ("the topic select has no label, line 24") — not general praise and not a wholesale rewrite.

"Audit this HTML form for missing labels, unsuitable input types, and keyboard problems."

Verify each finding in the browser before changing anything. A review you accepted without checking is just someone else's guess.

Common pitfalls

  • for pointing at name instead of id. Different attributes, different jobs. for matches id, always.
  • Duplicate id values after copying a field. The second label silently binds to the first input. Keep every id unique.
  • Removing the focus ring with outline: none because it looks untidy. Keyboard users then have no idea where they are. Replace it with a visible style — never delete it.
  • No name attribute. The control looks fine and its value is never submitted. Nothing visibly breaks, so it is easy to miss.

Verify it yourself

Open today's reference, MDN's Accessibility material, and find what it says about forms and labelling.

  1. MDN mentions aria-label as an alternative when no visible label is possible. Find its guidance on when that is acceptable, and why a real <label> is still preferred.
  2. Find MDN's description of what keyboard accessibility requires. Does your form meet every point?

Write both answers into notes/day-10.md, plus the one finding from your AI review that you confirmed with your own eyes.

The hour

  1. 0–5 min Recall

    Without notes, state yesterday’s main idea and one unresolved question.

  2. 5–20 min Learn

    Read only the listed concept notes and official reference sections needed today.

  3. 20–48 min Build

    Create a contact form with name, email, topic, and message. Test keyboard navigation.

  4. 48–55 min Explain and verify

    Run the result, inspect evidence, and explain the data/control flow in your own words.

  5. 55–60 min Quiz and commit

    Complete the quiz, record one lesson, and commit the verified change when applicable.

What to hand in

Deliverable

A keyboard-usable form with connected labels.

Working with AI today

AI as skeptical reviewer

Provide existing work and ask for concrete defects, risks, missing tests, and unsupported assumptions—not praise.

Audit this HTML form for missing labels, unsuitable input types, and keyboard problems.

References

End-of-day quiz

Q1 How should a label connect to an input?
Q2 Which result best proves today’s work is complete?
Q3 What should an AI code review primarily produce?

Explain-back gate

Pass the quiz above to unlock completion.

Quiz + explain-back checks required.