Mega Form Demo
This is a run-down of the accessible form structure, states, and validation we are shooting for. This is an attempt at an ideal outside of what a CMS or plugin might generate out of the box.
Form States
Basic Input
<div class="form-group"> <label class="control-label" for="inputName-1">Name</label> <input class="form-control" type="text" id="inputName-1"> </div>
Input Required
In our experience, using only ‘required’ isn’t enough for all screen readers. Make sure to add ‘aria-required’ as well.
<div class="form-group"> <label class="control-label" for="inputName-2">Name *</label> <input class="form-control" type="text" id="inputName-2" required aria-describedby="messageName-2 errorName-2"> <span class="message-block" id="messageName-2">Please enter your first name.</span> <span class="help-block hidden" id="errorName-2"></span> </div>
Required fields are followed by an *.
Input with Autocomplete
There are far more types of autocomplete levels, more shown below.
<div class="form-group"> <label class="control-label" for="inputName-3">Name</label> <input class="form-control" type="text" id="inputName-3" autocomplete="name"> </div>
Input with Error
Note - Using punctuation in help text and error messages helps screen readers speak them more naturally.
<div class="alert alert-danger" role="alert">
<p>Oops! Please check the following fields for errors and then resubmit the form:</p>
<ul>
<li><a href="#inputName-4">Name Required</a></li>
</ul>
</div>
<form action="">
<p>Required fields are followed by an *.</p>
<div class="form-group has-error has-feedback">
<label class="control-label" for="inputName-4">Name *</label>
<input class="form-control" type="text" id="inputName-4" required aria-describedby="errorName-4" aria-labelledby="messageName-4">
<span class="glyphicon glyphicon-warning-sign form-control-feedback" aria-hidden="true"></span>
<span class="message-block" id="messageName-4">Please enter your first name.</span>
<span class="help-block" id="errorName-4">Name required.</span>
</div>
</form>
Oops! Please check the following fields for errors and then resubmit the form:
Input with Multiple Error Messages
<div class="alert alert-danger" role="alert">
<p>Oops! Please check the following fields for errors and then resubmit the form:</p>
<ul>
<li><a href="#inputName-5">Name Required</a></li>
</ul>
</div>
<form action="">
<p>Required fields are followed by an *.</p>
<div class="form-group has-error has-feedback">
<label class="control-label" for="inputName-5">Name *</label>
<input class="form-control" type="text" id="inputName-5" required aria-describedby="messageName-5 errorName-5">
<span class="glyphicon glyphicon-warning-sign form-control-feedback" aria-hidden="true"></span>
<span class="message-block" id="messageName-5">Please enter your first name.</span>
<span class="help-block" id="errorName-5">Error one. Error two. Error three.</span>
</div>
</form>
Oops! Please check the following fields for errors and then resubmit the form:
Disabled Input
This input will be removed from the tab index, but will still be read by a screen reader.
<div class="form-group"> <label class="control-label" for="inputName-6">Name</label> <input class="form-control" type="text" id="inputName-6" disabled> </div>
Grouping
Fieldsets and legends can be used to semantically group a form into logical blocks and provide additional labeling. However, they should only be used when the form is complex enough that the groups are helpful.
Note - Screen readers seem to vary on how and when they actually read the legend. The best rule of thumb is to keep it short, and make the labels as clear as possible.
Grouped Sections of a Form
<fieldset>
<legend>Contact Information</legend>
<div class="form-group">
<label class="control-label" for="inputNoGroupName">Name</label>
<input class="form-control" type="text" id="inputNoGroupName">
</div>
<div class="form-group">
<label class="control-label" for="inputNoGroupEmail">Email</label>
<input class="form-control" type="email" id="inputNoGroupEmail">
</div>
</fieldset>
<fieldset>
<legend>Pizza Order</legend>
<fieldset class="form-group">
<legend>Select your pizza toppings:</legend>
<input id="hammy" type="checkbox" name="toppings" value="ham">
<label for="hammy">Ham</label><br>
<input id="pepperony" type="checkbox" name="toppings" value="pepperoni">
<label for="pepperony">Pepperoni</label><br>
<input id="mushroomy" type="checkbox" name="toppings" value="mushrooms">
<label for="mushroomy">Mushrooms</label><br>
<input id="olivey" type="checkbox" name="toppings" value="olives">
<label for="olivey">Olives</label>
</fieldset>
<fieldset class="form-group">
<legend>Select a size:</legend>
<input id="x-small" type="radio" name="sizes" value="x-small" checked>
<label for="x-small">X-Small</label><br>
<input id="small" type="radio" name="sizes" value="small">
<label for="small">Small</label><br>
<input id="medium" type="radio" name="sizes" value="medium">
<label for="medium">Medium</label><br>
<input id="large" type="radio" name="sizes" value="large">
<label for="large">Large</label><br>
<input id="x-large" type="radio" name="sizes" value="x-large">
<label for="x-large">X-Large</label>
</fieldset>
</fieldset>
<div class="form-group">
<input class="btn btn-primary" type="submit" value="Submit">
</div>
Exceptions - A form with just a few fields or without logical categories of information to combine into groups would not benefit from using a fieldset and legend. For example:
<div class="form-group"> <label class="control-label" for="inputNoGroupName">Name</label> <input class="form-control" type="text" id="inputNoGroupName"> </div> <div class="form-group"> <label class="control-label" for="inputNoGroupEmail">Email</label> <input class="form-control" type="email" id="inputNoGroupEmail"> </div> <div class="form-group"> <input class="btn btn-primary" type="submit" value="Submit"> </div>
Group of Checkboxes
<fieldset class="form-group"> <legend>Select your pizza toppings:</legend> <input id="hammy" type="checkbox" name="toppings" value="ham"> <label for="hammy">Ham</label><br> <input id="pepperony" type="checkbox" name="toppings" value="pepperoni"> <label for="pepperony">Pepperoni</label><br> <input id="mushroomy" type="checkbox" name="toppings" value="mushrooms"> <label for="mushroomy">Mushrooms</label><br> <input id="olivey" type="checkbox" name="toppings" value="olives"> <label for="olivey">Olives</label> </fieldset>
Exceptions - A single checkbox that has a descriptive label does not need a supporting fieldset and legend. For example:
<div class="form-group"> <input id="inputNewsletterSignUp" type="checkbox" name="queries" value="signup"> <label for="inputNewsletterSignUp">I would like to receive the Pizza Toppings Newsletter.</label> </div>
Group of Radio Buttons
<fieldset class="form-group"> <legend>Select a size:</legend> <input id="x-small" type="radio" name="sizes" value="x-small" checked> <label for="x-small">X-Small</label><br> <input id="small" type="radio" name="sizes" value="small"> <label for="small">Small</label><br> <input id="medium" type="radio" name="sizes" value="medium"> <label for="medium">Medium</label><br> <input id="large" type="radio" name="sizes" value="large"> <label for="large">Large</label><br> <input id="x-large" type="radio" name="sizes" value="x-large"> <label for="x-large">X-Large</label> </fieldset>
Exceptions - A small, simple set of choices with labels that are self-explanatory do not need a supporting fieldset and legend. For example:
<div class="form-group"> <input id="inputDelivery" type="radio" name="gender" value="Delivery" checked> <label for="inputDelivery">Delivery</label><br /> <input id="inputCarry-out" type="radio" name="gender" value="Carry-out"> <label for="inputCarry-out">Carry-out</label> </div>
Dropdowns
<div class="form-group">
<label for="selectExample1">Select how many pizzas you want: *</label>
<select class="form-control" id="selectExample1" required>
<option value="0">Choose the number of pizzas</option>
<option value="1">1 Pizza</option>
<option value="2">2 Pizzas</option>
<option value="3">3 Pizzas</option>
<option value="4">4 Pizzas</option>
<option value="5">5 Pizzas</option>
</select>
</div>