HTML forms collect user input and send it to a server. You build robust forms by pairing inputs with clear labels, choosing the right input types, using built-in validation, and providing helpful hint/error text. Accessible forms not only look good but also work well with screen readers, keyboards, and mobile devices.
action URL and HTTP method (get / post).for (on label) and id (on input) for accessibility.text, email, password, number, date, checkbox, radio, etc.required, minlength, pattern, as well as appropriate types.aria-describedby to explain what a field expects.
<!-- Simple contact form skeleton -->
<form action="/submit" method="post">
<label for="name">Full name</label>
<input id="name" name="name" type="text" required>
<label for="email">Email</label>
<input id="email" name="email" type="email" required>
<button type="submit">Send</button>
</form>
In real projects you often group inputs into sections (contact info, preferences, message, etc.) and add more attributes for validation and usability.
<form action="/submit" method="post" autocomplete="on">
<fieldset>
<legend>Contact details</legend>
<label for="name" class="req">Full name</label>
<input id="name" name="name" type="text" required autocomplete="name">
<div class="row">
<div>
<label for="email" class="req">Email</label>
<input id="email" name="email" type="email" required autocomplete="email">
</div>
<div>
<label for="phone">Phone</label>
<input id="phone" name="phone" type="text" inputmode="tel" autocomplete="tel">
</div>
</div>
<label for="password" class="req">Password</label>
<input
id="password"
name="password"
type="password"
required
minlength="8"
pattern="(?=.*[A-Za-z])(?=.*\d).{8,}"
aria-describedby="pwHelp">
<div id="pwHelp" class="help">At least 8 characters, with letters and a number.</div>
</fieldset>
<fieldset>
<legend>Preferences</legend>
<label for="role" class="req">Role</label>
<select id="role" name="role" required>
<option value="" disabled selected>Select a role</option>
<option>Student</option>
<option>Educator</option>
<option>Developer</option>
<option>Other</option>
</select>
<div class="inline" role="group" aria-labelledby="contactPref">
<span id="contactPref" class="sr-only">Preferred contact method</span>
<label><input type="radio" name="contact" value="email" checked> Email</label>
<label><input type="radio" name="contact" value="phone"> Phone</label>
</div>
<label>
<input type="checkbox" name="subscribe" value="yes">
Subscribe to updates
</label>
</fieldset>
<label for="message">
Message <span class="pill">optional</span>
</label>
<textarea
id="message"
name="message"
rows="3"
maxlength="500"
aria-describedby="msgHelp"></textarea>
<div id="msgHelp" class="help">Max 500 characters.</div>
<div class="actions">
<button type="submit">Submit</button>
<button type="reset">Reset</button>
</div>
</form>
This demo form is captured locally with JavaScript to show what data would be sent to a server. Try filling it out and clicking Submit.
<label> elements using matching for/id attributes.name attributes on all fields you want submitted to the server.email, tel, date) for better validation and mobile keyboards.<fieldset> and describe the group using <legend>.autocomplete attributes (name, email, tel, etc.) to improve UX.aria-describedby for complex requirements (password rules, limits, etc.).number input for age with min and max, and a date input for date of birth.topics[] (e.g., HTML, CSS, JS) and submit multiple selected values.setCustomValidity() API in JavaScript to display a custom error message when the password does not meet your rules.target="_blank" and pair it with rel="noopener".<form> element, inputs, labels, and buttons.