HTML Input Types
HTML5 introduced over a dozen input types that browsers handle with built-in UI and validation. Using the right input type is the single most impactful accessibility and UX improvement you can make to a form.
<!-- Text inputs -->
<input type="text" placeholder="Full name">
<input type="email" placeholder="email@example.com"> <!-- validates email format -->
<input type="password"> <!-- masks input -->
<input type="tel" placeholder="+1 555 0100"> <!-- numeric keyboard on mobile -->
<input type="url" placeholder="https://..."> <!-- validates URL format -->
<input type="number" min="1" max="100" step="1"> <!-- numeric spinbox -->
<input type="range" min="0" max="100"> <!-- slider -->
<input type="date"> <!-- date picker -->
<input type="color"> <!-- color picker -->
<input type="file" accept=".pdf,.jpg,.png"> <!-- file upload -->
<input type="checkbox"> <!-- boolean toggle -->
<input type="radio" name="plan" value="free"> <!-- one-of-many -->
<input type="hidden" name="csrf" value="token123"> <!-- invisible data -->
<textarea rows="4"></textarea> <!-- multi-line text -->
<select><option value="us">United States</option></select>
Accessible Form Structure
Every form input must have an associated label. Screen readers, keyboard navigation, and click-target size all depend on properly associated labels. Never skip labels — placeholder text is not a label.
<!-- ✅ Correct: label associated via 'for' and 'id' -->
<div class="form-group">
<label for="email">Email Address</label>
<input type="email" id="email" name="email" required autocomplete="email">
</div>
<!-- ✅ Alternative: label wraps input (implicit association) -->
<label>
Password
<input type="password" name="password" autocomplete="current-password">
</label>
<!-- ❌ Wrong: no label, only placeholder -->
<input type="email" placeholder="Enter email">
<!-- Fieldset for grouping related inputs -->
<fieldset>
<legend>Preferred contact method</legend>
<label><input type="radio" name="contact" value="email"> Email</label>
<label><input type="radio" name="contact" value="phone"> Phone</label>
</fieldset>
Built-in HTML Validation
<input
type="email"
required <!-- cannot be empty -->
minlength="5" <!-- minimum character count -->
maxlength="255" <!-- maximum character count -->
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$" <!-- regex pattern -->
aria-describedby="email-hint"
>
<p id="email-hint">Enter your work email address</p>
<!-- Custom validation messages with JavaScript -->
<script>
document.getElementById("email").addEventListener("invalid", (e) => {
e.target.setCustomValidity("Please enter a valid email address");
});
</script>
Handling Forms with JavaScript
const form = document.getElementById("signupForm");
form.addEventListener("submit", async (e) => {
e.preventDefault(); // stop default browser submit
// Read form data
const formData = new FormData(form);
const data = Object.fromEntries(formData); // { email: "...", name: "..." }
// Or read individual fields
const email = form.elements["email"].value;
// Submit to API
try {
const response = await fetch("/api/register", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
});
if (!response.ok) throw new Error("Registration failed");
window.location.href = "/dashboard";
} catch (error) {
showError(error.message);
}
});
Accessibility ChecklistEvery form should: use semantic labels, have visible focus styles, show error messages next to the relevant field, be completable with keyboard only, and not rely solely on color to indicate errors. Test with a screen reader at least once — NVDA (Windows) and VoiceOver (Mac) are free.