Module 3

Forms & Interactive HTML

Input Types • Labels • Validation • Fieldsets • Accessible Forms

📝 Why Forms Matter

Everything else on a web page — text, images, videos — is the website talking to you. Forms are how you talk back. Every login, every search, every checkout, every comment — they all start with a form.

But forms are more than just boxes on a screen. A well-built form is accessible, uses the right input types, and validates data before it's sent — all without a single line of JavaScript.

🏥
The Paper Form Analogy: Think of a form at a doctor's office. Each field has a label ("Name:"), a space to write (the input), and sometimes instructions ("Format: MM/DD/YYYY"). If the labels were missing, you'd stare at blank lines with no idea what to fill in. HTML forms work exactly the same way — every input needs a label, and every form needs a clear purpose.

🎯 By the end of this module, you will:

  • Build forms with <form>, action, and method
  • Pair every input with a proper <label>
  • Choose the right input type for each field
  • Use checkboxes, radio buttons, and dropdowns
  • Group related fields with <fieldset> and <legend>
  • Add built-in validation without JavaScript

📦 The <form> Element

The <form> element is a container that wraps all your inputs, labels, and buttons. It tells the browser: "This is a group of fields — when submitted, send the data somewhere."

<form action="/submit" method="POST">
  <label for="name">Name:</label>
  <input type="text" id="name" name="name">
  <button type="submit">Send</button>
</form>

Key Attributes

1 actionThe URL where the form data is sent. Example: action="/login". If omitted, the form submits to the current page.
2 methodHow the data is sent. Two options: GET or POST.

GET vs POST

📤 GET

Data is appended to the URL: /search?q=cats. Visible, bookmarkable, limited size. Use for searches and filters — anything the user might want to share or bookmark.

📥 POST

Data is sent in the request body — not visible in the URL. No size limit. Use for logins, registrations, and any form that changes data on the server.

How a Form Submits Data

<form action="/login" method="POST">
Email: alice@mail.com
Password: ●●●●●●●●
Log In
POST /login
email=alice@mail.com
&password=secret123
📝
Good to know: If you omit method, the default is GET. If you omit action, the form submits to the current page. The name attribute on each input is what the server receives — without it, that field's data is not sent.

🏷️ Labels & Inputs

The <label> + <input> pair is the fundamental building block of every form. A label tells the user (and screen readers) what a field is for. An input collects the data.

Two Ways to Connect Them

Explicit Label (Recommended)
<label for="email">Email:</label>
<input type="email" id="email" name="email">

The for attribute on the label matches the id on the input. This is the clearest and most reliable connection.

Implicit Label
<label>
  Email:
  <input type="email" name="email">
</label>

The input is inside the label — no for/id needed. Works well for compact layouts like checkboxes.

Why Labels Matter

1 Clickable — clicking a label focuses its input. For checkboxes and radios, it toggles the selection. This makes small targets much easier to hit.
2 Accessible — screen readers announce the label when the input receives focus. Without a label, blind users hear "edit text" — with no idea what to type.
3 Required by standards — WCAG accessibility guidelines require a visible label for every form control. It's not optional — it's a responsibility.
Placeholder ≠ Label A placeholder is grey hint text inside an input — it disappears when the user starts typing. It cannot replace a <label>. Users with cognitive disabilities, users who tab through forms, and screen readers all need a real, visible label. Placeholder is a hint, not a label.

🎨 Input Types Gallery

HTML5 gives you specialized input types that automatically show the right keyboard on mobile, provide built-in validation, and offer native UI like date pickers and color selectors. Always pick the most specific type.

Text Types

type="text" General-purpose text. The default — use it when no specialized type fits.
type="email" Validates email format automatically. Shows the @ keyboard on mobile devices.
type="password" Hides the typed characters with dots. Browsers may offer to save/autofill passwords.
type="search" Like text, but browsers may show a clear button (✕) and style it as a search field.
type="tel" Shows a phone keypad on mobile. No built-in validation — phone formats vary worldwide.
type="url" Validates URL format. Shows a keyboard with / and .com on mobile.

Specialized Types

type="number" Numeric input with spinner arrows. Use min, max, and step to constrain values.
type="date" Shows a native date picker. No need for a JavaScript date library.
type="time" Time selector with hours and minutes. The browser provides a native time picker UI.
type="range" A slider control. Perfect for volume, brightness, or any value within a range. Pair it with <output> to show the value.
type="color" Opens a native color picker. Returns a hex value like #ff6600.
type="file" File upload control. Use accept to filter types: accept="image/*" or accept=".pdf,.doc".

🧭 Which Input Type Should I Use?

1
Is the user choosing from predefined options?

Yes → 2–5 options: radio. 5+ options: <select>. Multiple allowed: checkbox. No → continue ↓

2
Is it a yes/no toggle?

Yes → type="checkbox" (single, standalone). No → continue ↓

3
Is it a number?

Exact value needed → type="number". Approximate range → type="range". No → continue ↓

4
Is it a date or time?

Yes → type="date" or type="time". No → continue ↓

5
Is it a file upload?

Yes → type="file". Use accept to filter types. No → continue ↓

6
Is it sensitive or hidden text?

Yes → type="password". The browser masks the characters. No → continue ↓

7
Is it a specific text format?

Email → type="email". Phone → type="tel". URL → type="url". Search → type="search". Color → type="color". No → continue ↓

None of the above?

Short text → type="text". Long text → <textarea>. These are your universal fallbacks.

📱
Mobile keyboards: On phones and tablets, the browser shows a different keyboard for each input type. type="email" shows the @ key, type="tel" shows the number pad, type="url" shows the .com key. This tiny HTML change dramatically improves the mobile experience.

☑️ Selection Controls

Beyond text inputs, forms need ways to choose from options. HTML gives you four selection patterns — each designed for a different scenario.

🔘 Pick ONE

The user must choose exactly one option from a group. Options are mutually exclusive.

radio (2–5 options) select (5+ options)

☑️ Pick MANY

The user can select zero or more options independently. Each choice is a separate toggle.

checkbox (any number) datalist (type + suggest)
<input type="checkbox">

A boolean toggle — on or off. Use for single yes/no choices, or for multiple options where the user can pick more than one. Each checkbox is independent.

<label>
  <input type="checkbox" name="agree">
  I agree to the terms
</label>
<input type="radio">

Choose exactly one from a group. Radio buttons with the same name attribute form a group — selecting one deselects the others.

<label><input type="radio" name="size" value="s"> Small</label>
<label><input type="radio" name="size" value="m"> Medium</label>
<label><input type="radio" name="size" value="l"> Large</label>
<select> + <option>

A dropdown list — choose one from many options. Best when you have 4+ predefined choices. Use <optgroup> to organize options into categories.

<label for="country">Country:</label>
<select id="country" name="country">
  <option value="">Choose...</option>
  <option value="fr">France</option>
  <option value="us">United States</option>
  <option value="jp">Japan</option>
</select>
<datalist>

Provides autocomplete suggestions for any input. The user can type freely OR pick a suggestion. Unlike <select>, the user is not limited to the provided options.

<input list="browsers" name="browser">
<datalist id="browsers">
  <option value="Chrome">
  <option value="Firefox">
  <option value="Safari">
</datalist>
⚠️
The name attribute groups radios: Radio buttons with the same name form a mutually exclusive group. If two radios have different names — they're independent, and the user can select both. This is one of the most common form bugs.

📄 Textareas & Other Fields

Not everything fits in a single-line input. Here are three more form elements you should know.

<textarea>

A multi-line text field — for comments, messages, bio, or any long-form text. Unlike <input>, the user can write paragraphs. Set initial size with rows and cols attributes.

<label for="msg">Message:</label>
<textarea id="msg" name="message" rows="4"></textarea>
<output>

Represents the result of a calculation or user action. Screen readers announce it as a live result. Often paired with type="range" to display the current slider value.

<label for="vol">Volume:</label>
<input type="range" id="vol" min="0" max="100" value="50">
<output>50</output>
<input type="hidden">

An invisible field that sends data the user doesn't need to see — like a session token, a form ID, or a timestamp. The value is included when the form is submitted but never displayed.

<input type="hidden" name="form_id" value="contact-v2">

🗂️ Form Structure

Large forms need organization. HTML gives you <fieldset> and <legend> to group related fields, and <button> to trigger actions.

Form Anatomy

Forms nest elements logically: <form> wraps everything, <fieldset> groups related fields, <legend> names each group, and <label> + <input> pairs collect the data.

<fieldset> + <legend>

Groups related form controls under a visible heading. Screen readers announce the legend when the user enters the group — so a field labeled "Street" inside a fieldset with legend "Shipping Address" is announced as "Shipping Address — Street".

<fieldset>
  <legend>Shipping Address</legend>
  <label for="street">Street:</label>
  <input type="text" id="street" name="street">
  <label for="city">City:</label>
  <input type="text" id="city" name="city">
</fieldset>
Shipping Address

The Three Button Types

type="submit" Sends the form data. This is the default type — a <button> inside a form submits it unless you specify otherwise.
type="reset" Clears all fields back to their default values. Rarely used — users almost never want to erase everything they typed.
type="button" Does nothing by default. Use it for JavaScript-powered actions (like "Add another item") that shouldn't submit or reset the form.
💡
Why <button> over <input type="submit">? A <button> can contain any HTML — text, icons, images. An <input type="submit"> can only display plain text via its value attribute. Always prefer <button> for flexibility and semantic clarity.

Putting It All Together

<form action="/register" method="POST">
  <fieldset>
    <legend>Personal Info</legend>
    <label for="fname">First Name:</label>
    <input type="text" id="fname" name="fname" required>
    <label for="lname">Last Name:</label>
    <input type="text" id="lname" name="lname" required>
  </fieldset>
  <fieldset>
    <legend>Account</legend>
    <label for="reg-email">Email:</label>
    <input type="email" id="reg-email" name="email" required>
    <label for="reg-pass">Password:</label>
    <input type="password" id="reg-pass" name="password" required minlength="8">
  </fieldset>
  <button type="submit">Create Account</button>
</form>

This form uses <fieldset> to group related fields, proper <label> pairing, specific input types, validation attributes, and a clear submit button. It's accessible, semantic, and functional.

✅ Built-in Validation

HTML can validate form data before it's submitted — no JavaScript needed. The browser checks the constraints you set and shows error messages in the user's language.

Validation Attributes

Attribute What It Does Example
required Field must be filled — empty submission is blocked. required
minlength / maxlength Minimum and maximum text length. minlength="3" maxlength="50"
min / max Minimum and maximum value for numbers and dates. min="18" max="120"
pattern A regex the value must match. pattern="[A-Z]{3}-\d{4}"
step Allowed number increments. step="0.01"
📝
Type-based validation: type="email" automatically checks for an @ symbol. type="url" checks for a valid URL format. type="number" rejects non-numeric input. You get validation for free just by choosing the right type.

Try It — Submit with Invalid Data

⚠️
Client-side ≠ Security HTML validation improves user experience — it catches mistakes before submission. But it is not security. Anyone can bypass it by editing the HTML in DevTools. Always validate again on the server.

♿ Accessible Forms

Forms are one of the hardest things for screen reader users to navigate. A few simple rules make the difference between a usable form and an impossible one.

The Form Accessibility Rules

1 Every input must have a label. No exceptions. Without a label, screen readers announce "edit text" — the user has no idea what to type. Placeholder is not a substitute.
2 Group related fields with <fieldset> + <legend>. When a screen reader enters a fieldset, it announces the legend — giving context. "Street" alone is ambiguous; "Shipping Address — Street" is clear.
3 Use aria-describedby for help text. If a field has instructions ("Password must be 8+ characters"), connect them: <input aria-describedby="pass-help"> + <span id="pass-help">...</span>. The screen reader reads both the label and the help text.
4 Use the autocomplete attribute. Values like autocomplete="email", autocomplete="given-name", and autocomplete="new-password" help browsers auto-fill forms — saving time and reducing errors for all users.
🔇 Screen Reader: No Labels

"Edit text. Edit text. Edit text. Button."
(No idea what each field is for. The user must guess.)

🔊 Screen Reader: Proper Labels

"Personal Info group. First name, required, edit text. Last name, required, edit text. Account group. Email, required, edit text. Create Account, button."
(Every field is announced with its label and context!)

💡
Key insight: An accessible form is a better form for everyone. Labels make inputs clickable. Fieldsets create visual grouping. Autocomplete saves time. Validation prevents mistakes. Accessibility isn't extra work — it's better design.

✏️ Practice: Building Better Forms

Look at each piece of HTML below. Identify the problems, then reveal the correct version.

Exercise 1: Fix the Form

This "form" uses only divs and has no accessibility. Rewrite it mentally with proper form elements, then reveal the answer.

<div class="form">
  <div class="row">
    <div>Name</div>
    <input type="text">
  </div>
  <div class="row">
    <div>Email</div>
    <input type="text">
  </div>
  <div class="row">
    <input type="text" placeholder="Your message...">
  </div>
  <div class="btn" onclick="send()">Send</div>
</div>

Exercise 2: Spot the Errors

This form uses proper tags but has five accessibility and structural mistakes. Find them all.

<form>
  <input type="text" placeholder="Your name">       <!-- ?? -->
  <label>Email</label>                       <!-- ?? -->
  <input type="email" id="user-email">
  <input type="radio" name="plan" value="free"> Free    <!-- ?? -->
  <input type="radio" name="pricing" value="pro"> Pro
  <input type="text">                       <!-- ?? -->
  <input type="submit" value="Sign Up">    <!-- ?? -->
</form>

Exercise 3: Choose the Right Input

For each scenario, which input type or element should you use?

1. The user's date of birth.

2. A star rating from 1 to 5.

3. The user's email address.

4. A long comment or feedback message.

5. Choosing a country from a list of 195 options.

📋 Module Summary

📦
The <form> Container

Wraps inputs, sends data with action (where) and method (GET for reads, POST for writes). The name attribute is what the server receives.

🏷️
Labels for Every Input

Use <label for="id"> to connect labels to inputs. Makes them clickable, accessible, and standards-compliant. Placeholder ≠ label.

🎨
Right Input Type

email, number, date, range, color — each type shows the right keyboard, provides validation, and offers native UI for free.

Built-in Validation

required, min/max, minlength/maxlength, pattern — HTML validates without JS. But always validate on the server too.

🗂️
Fieldsets & Buttons

<fieldset> + <legend> group related fields. <button> is preferred over <input type="submit"> — it can contain any HTML.

Accessible Forms

Every input needs a label. Group with fieldsets. Use aria-describedby for help text. Use autocomplete for faster filling.

Next Module →

Module 4: Tables, Media & Embedding

Data tables, audio, video, responsive images with srcset, iframes, and embedding maps and videos. Rich content for rich pages.