What We Build
A sign-up form for a fictional learning platform with five logical sections:
- Personal Details β first name, last name, date of birth, phone
- Account Setup β email, password, confirm password
- Address β street, city, postcode, country (select)
- Preferences β interests (checkboxes), experience level (radio), newsletter (range)
- Terms & Submit β avatar upload, terms checkbox, submit button
Section 1 β Personal Details
<form action="/register" method="POST" novalidate>
<fieldset>
<legend>Personal Details</legend>
<label for="firstName">First Name</label>
<input type="text" id="firstName" name="firstName"
required minlength="2" maxlength="50"
placeholder="Jane" autocomplete="given-name">
<label for="lastName">Last Name</label>
<input type="text" id="lastName" name="lastName"
required minlength="2" maxlength="50"
placeholder="Smith" autocomplete="family-name">
<label for="dob">Date of Birth</label>
<input type="date" id="dob" name="dob"
required min="1900-01-01" max="2010-12-31">
<label for="phone">Phone Number</label>
<input type="tel" id="phone" name="phone"
pattern="[\+]?[0-9\s\-\(\)]{7,20}"
placeholder="+44 7700 900000" autocomplete="tel">
</fieldset>
Section 2 β Account Setup
<fieldset>
<legend>Account Setup</legend>
<label for="email">Email Address</label>
<input type="email" id="email" name="email"
required autocomplete="email"
placeholder="jane@example.com">
<label for="password">Password</label>
<input type="password" id="password" name="password"
required minlength="8"
pattern="(?=.*[A-Z])(?=.*[0-9]).{8,}"
autocomplete="new-password"
placeholder="Min 8 chars, 1 uppercase, 1 number">
<label for="confirmPassword">Confirm Password</label>
<input type="password" id="confirmPassword" name="confirmPassword"
required autocomplete="new-password"
placeholder="Repeat password">
</fieldset>
Section 3 β Address
<fieldset>
<legend>Address</legend>
<label for="street">Street Address</label>
<input type="text" id="street" name="street"
placeholder="123 Main Street" autocomplete="street-address">
<label for="city">City</label>
<input type="text" id="city" name="city"
placeholder="London" autocomplete="address-level2">
<label for="postcode">Postcode</label>
<input type="text" id="postcode" name="postcode"
pattern="[A-Z]{1,2}[0-9][A-Z0-9]? ?[0-9][A-Z]{2}"
placeholder="SW1A 1AA" autocomplete="postal-code">
<label for="country">Country</label>
<select id="country" name="country" required autocomplete="country-name">
<option value="">-- Select country --</option>
<optgroup label="Common">
<option value="GB">United Kingdom</option>
<option value="US">United States</option>
<option value="CA">Canada</option>
<option value="AU">Australia</option>
</optgroup>
<optgroup label="Europe">
<option value="DE">Germany</option>
<option value="FR">France</option>
<option value="NL">Netherlands</option>
</optgroup>
</select>
</fieldset>
Section 4 β Preferences
<fieldset>
<legend>Preferences</legend>
<!-- Checkboxes: multiple interests -->
<p>Topics of interest:</p>
<label><input type="checkbox" name="interests" value="html"> HTML & CSS</label>
<label><input type="checkbox" name="interests" value="js"> JavaScript</label>
<label><input type="checkbox" name="interests" value="python"> Python</label>
<label><input type="checkbox" name="interests" value="owl"> OWL JS / Odoo</label>
<!-- Radio: experience level -->
<p>Experience level:</p>
<label><input type="radio" name="experience" value="beginner" required> Beginner</label>
<label><input type="radio" name="experience" value="intermediate"> Intermediate</label>
<label><input type="radio" name="experience" value="advanced"> Advanced</label>
<!-- Range: how many hours per week -->
<label for="hoursPerWeek">
Hours per week you can study:
<output id="hoursOutput">5</output>h
</label>
<input type="range" id="hoursPerWeek" name="hoursPerWeek"
min="1" max="40" value="5" step="1"
oninput="document.getElementById('hoursOutput').value = this.value">
<!-- Select: account type -->
<label for="accountType">Account type:</label>
<select id="accountType" name="accountType">
<option value="individual">Individual</option>
<option value="student">Student</option>
<option value="business">Business / Team</option>
</select>
</fieldset>
Section 5 β Terms & Submit
<fieldset>
<legend>Final Steps</legend>
<!-- File upload: avatar -->
<label for="avatar">Profile photo (optional)</label>
<input type="file" id="avatar" name="avatar"
accept="image/png, image/jpeg, image/webp">
<!-- Terms checkbox -->
<label>
<input type="checkbox" name="terms" required>
I agree to the
<a href="/terms.html" target="_blank" rel="noopener noreferrer">Terms of Service</a>
and
<a href="/privacy-policy.html" target="_blank" rel="noopener noreferrer">Privacy Policy</a>
</label>
<!-- Newsletter opt-in -->
<label>
<input type="checkbox" name="newsletter" value="yes">
Send me tips and updates by email
</label>
<!-- Hidden field for tracking -->
<input type="hidden" name="source" value="homepage-signup">
<button type="submit">Create My Account</button>
<button type="reset">Clear Form</button>
</fieldset>
</form>
Accessibility Notes
| Feature used | Why it matters |
|---|---|
<label for="id"> | Links label to input β screen readers announce label when input is focused |
<fieldset> + <legend> | Groups related fields β screen readers announce legend before each field in the group |
autocomplete | Browsers autofill from user's saved data β faster form completion, especially on mobile |
required | Browser validates before submit β communicates mandatory fields |
pattern | Custom format validation with regex |
placeholder | Shows expected format β never a replacement for a label |
HTML5 validation (required, pattern, type) can be bypassed by disabling JavaScript or sending raw HTTP requests. Always re-validate all form data on the server side before saving to a database or acting on it.
ποΈ Practical Exercise
- Build a form with name, email, and password fields.
- Associate a
<label>with each input. - Make all fields
required. - Add a password field with a
minlength. - Add a submit button and a "terms" checkbox.
π₯ Challenge Exercise
Build an accessible registration form: grouped <fieldset>s, labeled inputs, built-in validation (required, email type, password pattern), and a required terms checkbox. Submit invalid data to trigger browser validation. Explain why both client-side and server-side validation are needed.
π Summary
- Group related fields with
<fieldset>+<legend>for structure and accessibility. - Every input must have a matching
<label for="id">. - Use appropriate
typefor each field:email,password,date,tel,file, etc. - Add
autocompleteattributes β speeds up form completion on mobile. - Use
required,minlength,patternfor client-side validation β always validate server-side too. type="hidden"passes data the user doesn't see (tracking source, CSRF tokens).
Interview Questions
- What fields does a typical registration form include?
- How do you make form fields accessible with labels?
- What built-in validation can a registration form use?
- How would you validate that two password fields match?
- Why is server-side validation still required?
Related Topics
FAQ
Not directly β HTML has no "equals another field" rule. You compare the two values with a little JavaScript, or validate on the server. HTML handles required, length, pattern, and type checks.
No. It improves UX by catching mistakes early, but users can bypass it (disable JS, edit the page, or post directly). Always re-validate on the server.

