Nordburg Solutions

How to Write Accessible Forms

This tutorial can be viewed one part at a time or all in one page.

What's it all about?

When developing web applications, it's important that all users can use the application; those with low vision, those with no vision, those who are colour-blind, those who have mobility issues and can't use a mouse, those using a touch device and can't use a keyboard, and the temporarily-abled; ie: the rest of us.

The easiest disability to code for is, probably, colour-blindness. Just don't use colour alone to convey information. The most difficult disability would be blindness because you have to take measures to ensure that all information is presented to the user in an understandable way - usually text. So, all images that convey information must have meaningful and appropriate alternate text. Make sure instructions are clear and don't include references to visual cues.

This tutorial will attempt te teach how to write a form that will be accessible to all people.


First things First

The first thing to understand about HTML is that it is a semantic-based language. Each tag means something. A tag may have a default look to it, but that look has nothing to do with the meaning of the tag. The look can be changed with CSS. But the meaning is what's used by assistive technologies like screen readers. It is a mistake to think that if you want large, bold text, you should use an <h1> tag. You should use use an <h1> tag when you want a header for the whole page - regardless of what you want it to look like. You should use CSS to define what something should look like. So, it is important to understand what the meaning of each HTML tag is supposed to be used for.

The second thing to understand is that you can't determine how someone will view your site. Once upon a time it was common to see on a webpage something like "This page is best viewed using Netscape 3 with 640x480 resolution." Somehow we just accepted that. Then came the day when people only developed for Internet Explorer 6 because it had almost 100% market share. Both of those days are long gone. Now people can view your page on a smart phone programmed by Apple, Google, Microsfot, Blackberry, etc. Or they can view it on a desktop computer with a 24" monitor. Keeping this in mind, it shouldn't be too much of a leap to understand that some will have to have a screen reader read a page aloud to them.

Thirdly, while we're talking about screen readers, it's important to know that different people use different screen readers, and different screen readers interact differently with HTML. Furthermore, different users have different settings on their screen readers. Some people will hear title attributes. Some won't. For the purposes of this page, I'll mostly be referring to NVDA because it's the screen reader I'm most familiar with.

Lastly, most desktop screen readers use keyboard shortcuts to navigate around the page. Typically, you flow from top to bottom with the arrow keys. But once a form is encountered, and the user interacts with a form control the screen reader enters into forms mode. In forms mode, keystrokes interact with the form control itself. The only two ways to navigate through a form are: 1) After every form control, press Escape to get out of forms mode, navigate to the next form control with the arrow key, then enter forms mode again; or 2) press Tab to send focus from form control to form control until you get to the end of a form. For the purposes of this page, we'll assume that most users would use the second method because the first one would be a pain in the arse.

So, with these things in mind....


Let's Get Started

Note that everything you see in this tutorial is something I've seen "in the wild". I'm not making any of this up.


Building A Form For Input

We will build a registration form and make it accessible. A login form would consist of fields for a username, a full name, a password, retype your password, a Terms and Conditions checkbox, and a submit button. And just for fun we'll add gender and province select controls. We'll start with the worst form ever and slowly, step by step fix it so that it's accessible and WCAG 2.0 compliant.

Note that everything done here is something I've seen in the wild. I'm not making any of this up. Sadly, I won't be able to put all bone-headed examples in. It seems as though developers will seek out the worst, most inaccessible way to do things, and pat themselves on the back for having found a neat new way of doing things.

With all that having been said, let's start with our base form:

<form method="POST" action="#" name="theForm" id="theForm">
<span style="color: #FF0000; background-color: #FFFFFF;"><b>Username:</b></span>
<span class="input" onclick="focus();" onkeypress="addText(event, this);"></span>
<br>
<span style="color: #FF0000; background-color: #FFFFFF;"><b>Full Name:</b></span>
<span class="input" onkeypress="addText(event, this);">Full names must consist only of letters, a dash, and no more than 3 spaces.</span>
<br>
<span style="color: #FF0000; background-color: #FFFFFF;"><b>Password:</b></span>
<span class="input" onkeypress="addPasswordText(event, this);"></span>
<div>Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<span style="color: #FF0000; background-color: #FFFFFF;"><b>Re-type Password:</b></span>
<span class="input" onkeypress="addPasswordText(event, this);"></span>
<br> <span><b>What's your gender:</b></span>
<br>
<img src="accessibleForms/circle-unfilled.png" onclick="toggleCircle(this);" id="female" class="gender">
<span><b>Female</b></span>
<br>
<img src="accessibleForms/circle-unfilled.png" onclick="toggleCircle(this);" id="male" class="gender">
<span><b>Male</b></span>
<br>
<span><b>Where do you live?:</b></span>
<br>
<img src="accessibleForms/circle-unfilled.png" onclick="toggleCircle(this);" class="province" id="ab">
<span><b>Alberta:</b></span>
<br>
<img src="accessibleForms/circle-unfilled.png" onclick="toggleCircle(this);" class="province" id="bc">
<span><b>British Columbia</b></span>
<br>
<img src="accessibleForms/circle-unfilled.png" onclick="toggleCircle(this);" class="province" id="mn">
<span><b>Manitoba</b></span>
<br>
<img src="accessibleForms/circle-unfilled.png" onclick="toggleCircle(this);" class="province" id="nfld">
<span><b>Newfoundland</b></span>
<br>
<img src="accessibleForms/circle-unfilled.png" onclick="toggleCircle(this);" class="province" id="nb">
<span><b>New Brunswick</b></span>
<br>
<img src="accessibleForms/circle-unfilled.png" onclick="toggleCircle(this);" class="province" id="nwt">
<span><b>Northwest Territories</b></span>
<br>
<img src="accessibleForms/circle-unfilled.png" onclick="toggleCircle(this);" class="province" id="ns">
<span><b>Nova Scotia</b></span>
<br>
<img src="accessibleForms/circle-unfilled.png" onclick="toggleCircle(this);" class="province" id="nv">
<span><b>Nunavit</b></span>
<br>
<img src="accessibleForms/circle-unfilled.png" onclick="toggleCircle(this);" class="province" id="on">
<span><b>Ontario</b></span>
<br>
<img src="accessibleForms/circle-unfilled.png" onclick="toggleCircle(this);" class="province" id="pei">
<span><b>Prince Edward Island</b></span>
<br>
<img src="accessibleForms/circle-unfilled.png" onclick="toggleCircle(this);" class="province" id="qc">
<span><b>Quebec</b></span>
<br>
<img src="accessibleForms/circle-unfilled.png" onclick="toggleCircle(this);" class="province" id="sk">
<span><b>Saskatchewan</b></span>
<br>
<img src="accessibleForms/circle-unfilled.png" onclick="toggleCircle(this);" class="province" id="yk">
<span><b>Yukon</b></span>
<br> <span>Comments:</span>
<div class="input" onkeypress="addText(event, this);">Enter comments here.</div>
<div class="button" onclick="validateThenSubmit();">Submit</div>
<br>
<img src="accessibleForms/square-unchecked.png" onclick="toggleSquare(this); validateThenSubmit();"> <span style="color: #FF0000; background-color: #FFFFFF;"><b>I have read the <span class="likeLink" onclick="javascript:forwardURL('accessibleForms/tandc.php');">Terms and Conditions</span> and I agree to comply with them.</b></span>
</form>
<div>All required fields were in red.</div>

See this example in action.


Don't Re-invent The Wheel

Okay, taking a look at our base form, if you do this, you deserve to have every disability in the book thrust on you. It is a giant middle-finger to everyone with a disability, to anyone with a browser that sucks at interpreting Javascript, and to all competent web developers everywhere. And I hate you.

In this example I used spans with event handlers to modify their own content when a key is pressed. The problem is that you can't focus these items. They require Javascript to be turned on. While WCAG 2.0 does not require a Javascript-less interface, it's generally good practice to not require it more than necessary. Progressive enhancement and all that.

So, let's try not to re-invent the wheel. HTML since time immemorial has provided a text input, a password input, checkboxes, radio buttons, etc.. So, it should look like:

<form method="POST" action="#" name="theForm" id="theForm">
<span style="color: #FF0000; background-color: #FFFFFF;"><b>Username:</b></span>
<input type="text" name="uname">
<br>
<span style="color: #FF0000; background-color: #FFFFFF;"><b>Full Name:</b></span>
<input type="text" name="fname" placeholder="Full names must consist only of letters, a dash, and no more than 3 spaces."/>
<br>
<span style="color: #FF0000; background-color: #FFFFFF;"><b>Password:</b></span>
<input type="password" name="password"/>
<div>Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<span style="color: #FF0000; background-color: #FFFFFF;"><b>Re-type Password:</b></span>
<input type="password" name="repassword"/>
<br> <span><b>What's your gender:</b></span>
<br>
<input type="radio" name="gender" value="female">
<span><b>Female</b></span>
<br>
<input type="radio" name="gender" value="male">
<span><b>Male</b></span>
<br>
<span><b>Where do you live?:</b></span>
<br>
<input type="radio" name="province" value="ab">
<span><b>Alberta:</b></span>
<br>
<input type="radio" name="province" value="bc">
<span><b>British Columbia</b></span>
<br>
<input type="radio" name="province" value="mn">
<span><b>Manitoba</b></span>
<br>
<input type="radio" name="province" value="nfld">
<span><b>Newfoundland</b></span>
<br>
<input type="radio" name="province" value="nb">
<span><b>New Brunswick</b></span>
<br>
<input type="radio" name="province" value="nwt">
<span><b>Northwest Territories</b></span>
<br>
<input type="radio" name="province" value="ns">
<span><b>Nova Scotia</b></span>
<br>
<input type="radio" name="province" value="nv">
<span><b>Nunavit</b></span>
<br>
<input type="radio" name="province" value="on">
<span><b>Ontario</b></span>
<br>
<input type="radio" name="province" value="pei">
<span><b>Prince Edward Island</b></span>
<br>
<input type="radio" name="province" value="qc">
<span><b>Quebec</b></span>
<br>
<input type="radio" name="province" value="sk">
<span><b>Saskatchewan</b></span>
<br>
<input type="radio" name="province" value="yk">
<span><b>Yukon</b></span>
<br>
<span><b>Comments:</b></span>
<textarea name="comments">Enter comments here.</textarea>
<br>
<input type="submit" onclick="return validateGoodThenSubmit();" value="Submit">
<br>
<input type="checkbox" name="agree" onclick="validateGoodThenSubmit();" onblur="validateGoodThenSubmit();"> <span style="color: #FF0000; background-color: #FFFFFF;"><b>I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them.</b></span>
</form>
<div>All required fields were in red.</div>

See this example in action.


Don't Introduce Unexpected Behaviour

Okay, so there are no custom controls. Just native HTML. Even if you wanted to register, a blind user wouldn't hear very many useful instructions. They might hear something like "Username, Full Name, Editable, Editable password, password, clickable unselected clickable unselected (repeated for quite some time) Enter Comments Here.". Not very useful, is it?

While we're using native HTML elements, please get rid of that onclick event handler in the Terms and Conditions checkbox. What if a user wants to go back to make a correction? This idiotic form will submit before they can do anything! Again, using an HTML submit button is the way to go. Having an auto-submit form introduces unexpected behavior.

(Yes, you could go with a <button> with an event handler. But, again WHY? If you do this without good reason, you officially suck and should probably start looking for a new career.

<form method="POST" action="#" name="theForm" id="theForm">
<span style="color: #FF0000; background-color: #FFFFFF;"><b>Username:</b></span>
<input type="text" name="uname">
<br>
<span style="color: #FF0000; background-color: #FFFFFF;"><b>Full Name:</b></span>
<input type="text" name="fname" placeholder="Full names must consist only of letters, a dash, and no more than 3 spaces."/>
<br>
<span style="color: #FF0000; background-color: #FFFFFF;"><b>Password:</b></span>
<input type="password" name="password"/>
<div>Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<span style="color: #FF0000; background-color: #FFFFFF;"><b>Re-type Password:</b></span>
<input type="password" name="repassword"/>
<br> <span><b>What's your gender:</b></span>
<br>
<input type="radio" name="gender" value="female">
<span><b>Female</b></span>
<br>
<input type="radio" name="gender" value="male">
<span><b>Male</b></span>
<br>
<span><b>Where do you live?:</b></span>
<br>
<input type="radio" name="province" value="ab">
<span><b>Alberta:</b></span>
<br>
<input type="radio" name="province" value="bc">
<span><b>British Columbia</b></span>
<br>
<input type="radio" name="province" value="mn">
<span><b>Manitoba</b></span>
<br>
<input type="radio" name="province" value="nfld">
<span><b>Newfoundland</b></span>
<br>
<input type="radio" name="province" value="nb">
<span><b>New Brunswick</b></span>
<br>
<input type="radio" name="province" value="nwt">
<span><b>Northwest Territories</b></span>
<br>
<input type="radio" name="province" value="ns">
<span><b>Nova Scotia</b></span>
<br>
<input type="radio" name="province" value="nv">
<span><b>Nunavit</b></span>
<br>
<input type="radio" name="province" value="on">
<span><b>Ontario</b></span>
<br>
<input type="radio" name="province" value="pei">
<span><b>Prince Edward Island</b></span>
<br>
<input type="radio" name="province" value="qc">
<span><b>Quebec</b></span>
<br>
<input type="radio" name="province" value="sk">
<span><b>Saskatchewan</b></span>
<br>
<input type="radio" name="province" value="yk">
<span><b>Yukon</b></span>
<br>
<span><b>Comments:</b></span>
<textarea name="commentS">Enter comments here.</textarea>
<br>
<input type="submit" onclick="return validateGoodThenSubmit();" value="Submit">
<br>
<input type="checkbox" name="agree"> <span style="color: #FF0000; background-color: #FFFFFF;"><b>I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them.</b></span>
</form>
<div>All required fields were in red.</div>

See this example in action.


Use Correct HTML Elements

Okay, so the form we have now is using correct input types and a submit button. A keyboard-only visual user will be able to use this form. But a blind user will still be lost.

Thankfully HTML provides labels. When a screen reader gets to a form control while in forms mode, it reads the label aloud to tell the user what the form control is.

A label is associate with one and only one form control. Some form controls (buttons, for example) have implicit labels. The hidden form control does not need a label. But checkboxes, radio buttons, textareas, textboxes, drop-down boxes, etc. all require lables.

Labels can be associated with form controls explicitly or implicitly. Implicit associations are usually used for radio buttons and checkboxes. Having the form control as a child element of the label makes an implicit association. Using the for attribute in the label element explicitly associates the label with a form control. The form control's id value is the calue for the for attribute in the label.

So, let's re-do the above form using labels instead of spans. The submit button will use the value attribute for the label. The other two will be explicitly associated.

<form method="POST" action="#" name="theForm" id="theForm">
<label for="username" style="color: #FF0000; background-color: #FFFFFF;"><b>Username:</b></label>
<input type="text" name="uname" id="username">
<br>
<label for="firstname" style="color: #FF0000; background-color: #FFFFFF;"><b>Full Name:</b></label>
<input type="text" name="fname" id="firstname" placeholder="Full names must consist only of letters, a dash, and no more than 3 spaces."/>
<br>
<label for="password1" style="color: #FF0000; background-color: #FFFFFF;"><b>Password:</b></label>
<input type="password" name="password" id="password1"/>
<div>Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<label for="password2" style="color: #FF0000; background-color: #FFFFFF;"><b>Re-type Password:</b></label>
<input type="password" name="repassword" id="password2"/>
<br> <span><b>What's your gender:</b></span>
<br>
<label>
<input type="radio" name="gender" value="female">
<b>Female</b></label>
<br>
<label>
<input type="radio" name="gender" value="male">
<b>Male</b></label>
<br>
<span><b>Where do you live?:</b></span>
<br>
<label>
<input type="radio" name="province" value="ab">
<b>Alberta:</b></label>
<br>
<label>
<input type="radio" name="province" value="bc">
<b>British Columbia</b></label>
<br>
<label>
<input type="radio" name="province" value="mn">
<b>Manitoba</b></label>
<br>
<label>
<input type="radio" name="province" value="nfld">
<b>Newfoundland</b></label>
<br>
<label>
<input type="radio" name="province" value="nb">
<b>New Brunswick</b></label>
<br>
<label>
<input type="radio" name="province" value="nwt">
<b>Northwest Territories</b></label>
<br>
<label>
<input type="radio" name="province" value="ns">
<b>Nova Scotia</b></label>
<br>
<label>
<input type="radio" name="province" value="nv">
<b>Nunavit</b></label>
<br>
<label>
<input type="radio" name="province" value="on">
<b>Ontario</b></label>
<br>
<label>
<input type="radio" name="province" value="pei">
<b>Prince Edward Island</b></label>
<br>
<label>
<input type="radio" name="province" value="qc">
<b>Quebec</b></label>
<br>
<label>
<input type="radio" name="province" value="sk">
<b>Saskatchewan</b></label>
<br>
<label>
<input type="radio" name="province" value="yk">
<b>Yukon</b></label>
<br>
<label for="comments"><b>Comments:</b></label>
<textarea id="comments" name="comments">Enter comments here.</textarea>
<br>
<input type="submit" onclick="return validateGoodThenSubmit();" value="Submit">
<br>
<label style="color: #FF0000; background-color: #FFFFFF;">
<input type="checkbox" name="agree"> <b>I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them.</b></label>
</form>
<div>All required fields were in red.</div>

See this example in action.

Now a screen reader will read out something like: "Username editable, Full Name editable, Password Editable password, Re-type Password editable password, Female clickable unselected Male clickable unselected Female clickable unselected Male clickable unselected " followed by all the provinces and territories at least once with "unselected" followed by "Comments, Enter comments here, clickable unchecked I have read the Link Terms and Conditions and I agree to comply with them, Submit clickable."


Know when to use radio buttons and when to use a drop-down

We're on our way, but the way it reads out all of those provinces is annoying. And there's only ten of them (and three territories). In the states, there are fifty of them. There has to be a better way! And there is. A select, or drop-down box. Radio buttons are okay when there are only two or three options, like with gender. The form only gives two options. Whenever one is selected, the whole list is read by the screen reader. Not a problem for two items, but a big problem for longer lists. So, for provinces (or states) we'll use a select box.

<form method="POST" action="#" name="theForm" id="theForm">
<label for="username" style="color: #FF0000; background-color: #FFFFFF;"><b>Username:</b></label>
<input type="text" name="uname" id="username">
<br>
<label for="firstname" style="color: #FF0000; background-color: #FFFFFF;"><b>Full Name:</b></label>
<input type="text" name="fname" id="firstname" placeholder="Full names must consist only of letters, a dash, and no more than 3 spaces."/>
<br>
<label for="password1" style="color: #FF0000; background-color: #FFFFFF;"><b>Password:</b></label>
<input type="password" name="password" id="password1"/>
<div>Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<label for="password2" style="color: #FF0000; background-color: #FFFFFF;"><b>Re-type Password:</b></label>
<input type="password" name="repassword" id="password2"/>
<br> <span><b>What's your gender:</b></span>
<br>
<label>
<input type="radio" name="gender" value="female">
<b>Female</b></label>
<br>
<label>
<input type="radio" name="gender" value="male">
<b>Male</b></label>
<br>
<label for="province"><b>Where do you live?:</b></label>
<select id="province" name="prov">
<option></option>
<option value="ab">Alberta:</option>
<option value="bc">British Columbia</option>
<option value="mn">Manitoba</option>
<option value="nfld">Newfoundland</option>
<option value="nb">New Brunswick</option>
<option value="nwt">Northwest Territories</option>
<option value="ns">Nova Scotia</option>
<option value="nv">Nunavit</option>
<option value="on">Ontario</option>
<option value="pei">Prince Edward Island</option>
<option value="qc">Quebec</option>
<option value="sk">Saskatchewan</option>
<option value="yk">Yukon</option>
</select>
<br>
<label for="comments"><b>Comments:</b></label>
<textarea id="comments" name="comments">Enter comments here.</textarea>
<br>
<input type="submit" onclick="return validateBetterThenSubmit();" value="Submit">
<br>
<label style="color: #FF0000; background-color: #FFFFFF;">
<input type="checkbox" name="agree"> <b>I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them.</b></label>
</form>
<div>All required fields were in red.</div>

See this example in action.


Maintain Valid HTML

Almost! Notice the first item in the drop-down is empty? That should never be. If you don't want the first item being one that you can select, then put a place-holder in there, and check for the value in form validation after submission.

<form method="POST" action="#" name="theForm" id="theForm">
<label for="username" style="color: #FF0000; background-color: #FFFFFF;"><b>Username:</b></label>
<input type="text" name="uname" id="username">
<br>
<label for="firstname" style="color: #FF0000; background-color: #FFFFFF;"><b>Full Name:</b></label>
<input type="text" name="fname" id="firstname" placeholder="Full names must consist only of letters, a dash, and no more than 3 spaces."/>
<br>
<label for="password1" style="color: #FF0000; background-color: #FFFFFF;"><b>Password:</b></label>
<input type="password" name="password" id="password1"/>
<div>Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<label for="password2" style="color: #FF0000; background-color: #FFFFFF;"><b>Re-type Password:</b></label>
<input type="password" name="repassword" id="password2"/>
<br> <span><b>What's your gender:</b></span>
<br>
<label>
<input type="radio" name="gender" value="female">
<b>Female</b></label>
<br>
<label>
<input type="radio" name="gender" value="male">
<b>Male</b></label>
<br>
<label for="province"><b>Where do you live?:</b></label>
<select id="province" name="prov">
<option value="false">Select Province</option>
<option value="ab">Alberta:</option>
<option value="bc">British Columbia</option>
<option value="mn">Manitoba</option>
<option value="nfld">Newfoundland</option>
<option value="nb">New Brunswick</option>
<option value="nwt">Northwest Territories</option>
<option value="ns">Nova Scotia</option>
<option value="nv">Nunavit</option>
<option value="on">Ontario</option>
<option value="pei">Prince Edward Island</option>
<option value="qc">Quebec</option>
<option value="sk">Saskatchewan</option>
<option value="yk">Yukon</option>
</select>
<br>
<label for="comments"><b>Comments:</b></label>
<textarea id="comments" name="comments">Enter comments here.</textarea>
<br>
<input type="submit" onclick="return validateBetterThenSubmit();" value="Submit">
<br>
<label style="color: #FF0000; background-color: #FFFFFF;">
<input type="checkbox" name="agree"> <b>I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them.</b></label>
</form>
<div>All required fields were in red.</div>

See this example in action.


What about 'in-between' text?

Now a screen reader will read out something like: "Username editable, Fullname, Password Editable password, Re-type Password editable password, Female clickable unselected Male clickable unselected Female clickable unselected Male clickable unselected, Where do you live? Select Province Drop-down, Comments, Enter comments here, clickable unchecked I have read the Link Terms and Conditions and I agree to comply with them, Submit clickable."

If you're paying close attention you'll see it's missing a few things like password requirements, "What's your Gender?", and "All required fields were in red." First, any instructions about the form should come before the form! So we'll just move that up top. While we're at it, put that Terms and Conditions checkbox before the submit button!

As for the "What's your gender?", well, this could have been in the User Proper HTML section. When you have a group of form controls that are closely related (like a group of radio buttons), or a group of form controls that have the same text to "bind them all together" (if you will), then a fieldset and legend are what you're looking for. The fieldset is what binds them all together, and the legend is what is read before each label.

<form method="POST" action="#" name="theForm" id="theForm">
<div>All required fields are in red.</div>
<label for="username" style="color: #FF0000; background-color: #FFFFFF;"><b>Username:</b></label>
<input type="text" name="uname" id="username">
<br>
<label for="firstname" style="color: #FF0000; background-color: #FFFFFF;"><b>Full Name:</b></label>
<input type="text" name="fname" id="firstname" placeholder="Full names must consist only of letters, a dash, and no more than 3 spaces."/>
<br>
<label for="password1" style="color: #FF0000; background-color: #FFFFFF;"><b>Password:</b></label>
<input type="password" name="password" id="password1"/>
<div>Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<label for="password2" style="color: #FF0000; background-color: #FFFFFF;"><b>Re-type Password:</b></label>
<input type="password" name="repassword" id="password2"/>
<fieldset>
<legend><b>What's your gender:</b></legend>
<br>
<label>
<input type="radio" name="gender" value="female">
<b>Female</b></label>
<br>
<label>
<input type="radio" name="gender" value="male">
<b>Male</b></label>
</fieldset>
<label for="province"><b>Where do you live?:</b></label>
<select id="province" name="prov">
<option value="false">Select Province</option>
<option value="ab">Alberta:</option>
<option value="bc">British Columbia</option>
<option value="mn">Manitoba</option>
<option value="nfld">Newfoundland</option>
<option value="nb">New Brunswick</option>
<option value="nwt">Northwest Territories</option>
<option value="ns">Nova Scotia</option>
<option value="nv">Nunavit</option>
<option value="on">Ontario</option>
<option value="pei">Prince Edward Island</option>
<option value="qc">Quebec</option>
<option value="sk">Saskatchewan</option>
<option value="yk">Yukon</option>
</select>
<br>
<label for="comments"><b>Comments:</b></label>
<textarea id="comments" name="comments">Enter comments here.</textarea>
<br>
<label style="color: #FF0000; background-color: #FFFFFF;">
<input type="checkbox" name="agree"> <b>I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them.</b></label>
<br>
<input type="submit" onclick="return validateBetterThenSubmit();" value="Submit">
</form>

See this example in action.


What About Those Password Instructions?

Now a screen reader will read out something like: "All required fields are in red. Username editable, Full Name editable, Password Editable password, Re-type Password editable password, What's your gender? Female clickable unselected Male clickable unselected Female clickable unselected Male clickable unselected, Where do you live? Select Province Drop-down, Comments, Enter comments here, clickable unchecked I have read the Link Terms and Conditions and I agree to comply with them, Submit clickable."

Now we're getting somwhere! But we're still missing the password instructions. The simplest, most basic way is to make it selectable with a tabindex="0". (There is a more advanced way, but I'll deal with that later.)

The reason we use tabindex="0" is that it makes otherwise non-focusable text focusable. As the form is right now, after you tab out of Password, you tab into Re-type Password.

Important note about tabindex="0": Focusable elements are usually interactive elements. By adding tabindex="0" to a non-interactive component (like a <p> tag or an <h2> for example) is almost like lying to the user, and should be avoided at all costs....unless it's the best way to do something, like providing instructions or information in the middle of a form for a set of form controls.

<form method="POST" action="#" name="theForm" id="theForm">
<div>All required fields are in red.</div>
<label for="username" style="color: #FF0000; background-color: #FFFFFF;"><b>Username:</b></label>
<input type="text" name="uname" id="username">
<br>
<label for="firstname" style="color: #FF0000; background-color: #FFFFFF;"><b>Full Name:</b></label>
<input type="text" name="fname" id="firstname" placeholder="Full names must consist only of letters, a dash, and no more than 3 spaces."/>
<br>
<label for="password1" style="color: #FF0000; background-color: #FFFFFF;"><b>Password:</b></label>
<input type="password" name="password" id="password1"/>
<div tabindex="0">Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<label for="password2" style="color: #FF0000; background-color: #FFFFFF;"><b>Re-type Password:</b></label>
<input type="password" name="repassword" id="password2"/>
<fieldset>
<legend><b>What's your gender:</b></legend>
<br>
<label>
<input type="radio" name="gender" value="female">
<b>Female</b></label>
<br>
<label>
<input type="radio" name="gender" value="male">
<b>Male</b></label>
</fieldset>
<label for="province"><b>Where do you live?:</b></label>
<select id="province" name="prov">
<option value="false">Select Province</option>
<option value="ab">Alberta:</option>
<option value="bc">British Columbia</option>
<option value="mn">Manitoba</option>
<option value="nfld">Newfoundland</option>
<option value="nb">New Brunswick</option>
<option value="nwt">Northwest Territories</option>
<option value="ns">Nova Scotia</option>
<option value="nv">Nunavit</option>
<option value="on">Ontario</option>
<option value="pei">Prince Edward Island</option>
<option value="qc">Quebec</option>
<option value="sk">Saskatchewan</option>
<option value="yk">Yukon</option>
</select>
<br>
<label for="comments"><b>Comments:</b></label>
<textarea id="comments" name="comments">Enter comments here.</textarea>
<br>
<label style="color: #FF0000; background-color: #FFFFFF;">
<input type="checkbox" name="agree"> <b>I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them.</b></label>
<br>
<input type="submit" onclick="return validateBetterThenSubmit();" value="Submit">
</form>

See this example in action.

Now a screen reader will read out something like: "All required fields are in red. Username editable, Full Name editable, Password Editable password, Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess. Re-type Password editable password, What's your gender? Female clickable unselected Male clickable unselected Female clickable unselected Male clickable unselected, Where do you live? Select Province Drop-down, Comments, Enter comments here, clickable unchecked I have read the Link Terms and Conditions and I agree to comply with them, Submit clickable."


Colouring

Right now all the required fields are in red. Pure red (#FF0000) on white (#FFFFFF). The problem is is that this gives too low a colour contrast ratio for people with low vision. Change that #FF0000 to a darker red, say ##CC0000, or #AA0000.

<form method="POST" action="#" name="theForm" id="theForm">
<div>All required fields are in red.</div>
<label for="username" style="color: #CC0000; background-color: #FFFFFF;"><b>Username:</b></label>
<input type="text" name="uname" id="username">
<br>
<label for="firstname" style="color: #CC0000; background-color: #FFFFFF;"><b>Full Name:</b></label>
<input type="text" name="fname" id="firstname" placeholder="Full names must consist only of letters, a dash, and no more than 3 spaces."/>
<br>
<label for="password1" style="color: #CC0000; background-color: #FFFFFF;"><b>Password:</b></label>
<input type="password" name="password" id="password1"/>
<div tabindex="0">Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<label for="password2" style="color: #CC0000; background-color: #FFFFFF;"><b>Re-type Password:</b></label>
<input type="password" name="repassword" id="password2"/>
<fieldset>
<legend><b>What's your gender:</b></legend>
<br>
<label>
<input type="radio" name="gender" value="female">
<b>Female</b></label>
<br>
<label>
<input type="radio" name="gender" value="male">
<b>Male</b></label>
</fieldset>
<label for="province"><b>Where do you live?:</b></label>
<select id="province" name="prov">
<option value="false">Select Province</option>
<option value="ab">Alberta:</option>
<option value="bc">British Columbia</option>
<option value="mn">Manitoba</option>
<option value="nfld">Newfoundland</option>
<option value="nb">New Brunswick</option>
<option value="nwt">Northwest Territories</option>
<option value="ns">Nova Scotia</option>
<option value="nv">Nunavit</option>
<option value="on">Ontario</option>
<option value="pei">Prince Edward Island</option>
<option value="qc">Quebec</option>
<option value="sk">Saskatchewan</option>
<option value="yk">Yukon</option>
</select>
<br>
<label for="comments"><b>Comments:</b></label>
<textarea id="comments" name="comments">Enter comments here.</textarea>
<br>
<label style="color: #CC0000; background-color: #FFFFFF;">
<input type="checkbox" name="agree"> <b>I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them.</b></label>
<br>
<input type="submit" onclick="return validateBetterThenSubmit();" value="Submit">
</form>

See this example in action.


Remove Colouring

Great, now low vision people can see which fields are required. But not colour-blind people. It's best to just put '(Required)' in the label. (You can remove that 'Required fields are in red' bit too.)

<form method="POST" action="#" name="theForm" id="theForm">
<label for="username"><b>Username: (Required)</b></label>
<input type="text" name="uname" id="username">
<br>
<label for="firstname"><b>Full Name: (Required)</b></label>
<input type="text" name="fname" id="firstname" placeholder="Full names must consist only of letters, a dash, and no more than 3 spaces."/>
<br>
<label for="password1"><b>Password: (Required)</b></label>
<input type="password" name="password" id="password1"/>
<div tabindex="0">Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<label for="password2"><b>Re-type Password: (Required)</b></label>
<input type="password" name="repassword" id="password2"/>
<fieldset>
<legend><b>What's your gender:</b></legend>
<br>
<label>
<input type="radio" name="gender" value="female">
<b>Female</b></label>
<br>
<label>
<input type="radio" name="gender" value="male">
<b>Male</b></label>
</fieldset>
<label for="province"><b>Where do you live?:</b></label>
<select id="province" name="prov">
<option value="false">Select Province</option>
<option value="ab">Alberta:</option>
<option value="bc">British Columbia</option>
<option value="mn">Manitoba</option>
<option value="nfld">Newfoundland</option>
<option value="nb">New Brunswick</option>
<option value="nwt">Northwest Territories</option>
<option value="ns">Nova Scotia</option>
<option value="nv">Nunavit</option>
<option value="on">Ontario</option>
<option value="pei">Prince Edward Island</option>
<option value="qc">Quebec</option>
<option value="sk">Saskatchewan</option>
<option value="yk">Yukon</option>
</select>
<br>
<label for="comments"><b>Comments:</b></label>
<textarea id="comments" name="comments">Enter comments here.</textarea>
<br>
<label>
<input type="checkbox" name="agree"> <b>I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them. (Required)</b></label>
<br>
<input type="submit" onclick="return validateBetterThenSubmit();" value="Submit">
</form>

See this example in action.


Use of Symbols

Perhaps you don't want '(Required)' written out for every field that is required. Many websites use an asterisk. If you do so, don't forget to put a note at the top of the page saying what the asterisk means.

Keep in mind that some screen readers might not read out the asterisk because of verbosity levels. So, it's would be best to leave '(Required') in, but hide it from visual users.

<form method="POST" action="#" name="theForm" id="theForm">
<div>Fields marked with <span style="color: #CC0000; background-color: #FFFFFF;">*</span> are required.</div>
<label for="username"><b>Username: <span style="color: #CC0000; background-color: #FFFFFF;">*</span> <span style="display: none;">(Required)</span></b></label>
<input type="text" name="uname" id="username">
<br>
<label for="firstname"><b>Full Name: <span style="color: #CC0000; background-color: #FFFFFF;">*</span> <span style="display: none;">(Required)</span></b></label>
<input type="text" name="fname" id="firstname" placeholder="Full names must consist only of letters, a dash, and no more than 3 spaces."/>
<br>
<label for="password1"><b>Password: <span style="color: #CC0000; background-color: #FFFFFF;">*</span> <span style="display: none;">(Required)</span></b></label>
<input type="password" name="password" id="password1"/>
<div tabindex="0">Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<label for="password2"><b>Re-type Password: <span style="color: #CC0000; background-color: #FFFFFF;">*</span> <span style="display: none;">(Required)</span></b></label>
<input type="password" name="repassword" id="password2"/>
<fieldset>
<legend><b>What's your gender:</b></legend>
<br>
<label>
<input type="radio" name="gender" value="female">
<b>Female</b></label>
<br>
<label>
<input type="radio" name="gender" value="male">
<b>Male</b></label>
</fieldset>
<label for="province"><b>Where do you live?:</b></label>
<select id="province" name="prov">
<option value="false">Select Province</option>
<option value="ab">Alberta:</option>
<option value="bc">British Columbia</option>
<option value="mn">Manitoba</option>
<option value="nfld">Newfoundland</option>
<option value="nb">New Brunswick</option>
<option value="nwt">Northwest Territories</option>
<option value="ns">Nova Scotia</option>
<option value="nv">Nunavit</option>
<option value="on">Ontario</option>
<option value="pei">Prince Edward Island</option>
<option value="qc">Quebec</option>
<option value="sk">Saskatchewan</option>
<option value="yk">Yukon</option>
</select>
<br>
<label for="comments"><b>Comments:</b></label>
<textarea id="comments" name="comments">Enter comments here.</textarea>
<br>
<label>
<input type="checkbox" name="agree"> <b>I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them. <span style="color: #CC0000; background-color: #FFFFFF;">*</span> <span style="display: none;">(Required)</span></b></label>
<br>
<input type="submit" onclick="return validateBetterThenSubmit();" value="Submit">
</form>

See this example in action.


Invisible Stuff

In order to hide the '(Required)' text from visual users we used styling to set the display property to 'none'. This will hide the text from visual users for sure, but it will also hide it from screen reader users! So, that's no good! We can use another type of styling to hide the next from visual users, but leave it readable by screen readers. We're use style="position: absolute; height: 1px; width: 1px; clip: rect(1px, 1px, 1px, 1px); overflow: hidden; margin: 0;"

<form method="POST" action="#" name="theForm" id="theForm">
<div>Fields marked with <span style="color: #CC0000; background-color: #FFFFFF;">*</span> are required.</div>
<label for="username"><b>Username: <span style="color: #CC0000; background-color: #FFFFFF;">*</span> <span style="position: absolute; height: 1px; width: 1px; clip: rect(1px, 1px, 1px, 1px); overflow: hidden; margin: 0;">(Required)</span></b></label>
<input type="text" name="uname" id="username">
<br>
<label for="firstname"><b>Full Name: <span style="color: #CC0000; background-color: #FFFFFF;">*</span> <span style="position: absolute; height: 1px; width: 1px; clip: rect(1px, 1px, 1px, 1px); overflow: hidden; margin: 0;">(Required)</span></b></label>
<input type="text" name="fname" id="firstname" placeholder="Full names must consist only of letters, a dash, and no more than 3 spaces."/>
<br>
<label for="password1"><b>Password: <span style="color: #CC0000; background-color: #FFFFFF;">*</span> <span style="position: absolute; height: 1px; width: 1px; clip: rect(1px, 1px, 1px, 1px); overflow: hidden; margin: 0;">(Required)</span></b></label>
<input type="password" name="password" id="password1"/>
<div tabindex="0">Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<label for="password2"><b>Re-type Password: <span style="color: #CC0000; background-color: #FFFFFF;">*</span> <span style="position: absolute; height: 1px; width: 1px; clip: rect(1px, 1px, 1px, 1px); overflow: hidden; margin: 0;">(Required)</span></b></label>
<input type="password" name="repassword" id="password2"/>
<fieldset>
<legend><b>What's your gender:</b></legend>
<br>
<label>
<input type="radio" name="gender" value="female">
<b>Female</b></label>
<br>
<label>
<input type="radio" name="gender" value="male">
<b>Male</b></label>
</fieldset>
<label for="province"><b>Where do you live?:</b></label>
<select id="province" name="prov">
<option value="false">Select Province</option>
<option value="ab">Alberta:</option>
<option value="bc">British Columbia</option>
<option value="mn">Manitoba</option>
<option value="nfld">Newfoundland</option>
<option value="nb">New Brunswick</option>
<option value="nwt">Northwest Territories</option>
<option value="ns">Nova Scotia</option>
<option value="nv">Nunavit</option>
<option value="on">Ontario</option>
<option value="pei">Prince Edward Island</option>
<option value="qc">Quebec</option>
<option value="sk">Saskatchewan</option>
<option value="yk">Yukon</option>
</select>
<br>
<label for="comments"><b>Comments:</b></label>
<textarea id="comments" name="comments">Enter comments here.</textarea>
<br>
<label>
<input type="checkbox" name="agree"> <b>I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them. <span style="color: #CC0000; background-color: #FFFFFF;">*</span> <span style="position: absolute; height: 1px; width: 1px; clip: rect(1px, 1px, 1px, 1px); overflow: hidden; margin: 0;">(Required)</span></b></label>
<br>
<input type="submit" onclick="return validateBetterThenSubmit();" value="Submit">
</form>

See this example in action.


Proper Coding

Our page is getting pretty messy to look at with all that inline styling. Instead we'll use a .css style sheet to define our styles. 'style="color: #CC0000; background-color: #FFFFFF;"' will be replaced by:

.red { color: #CC0000; background-color: #FFFFFF; }

And 'style="position: absolute; height: 1px; width: 1px; clip: rect(1px, 1px, 1px, 1px); overflow: hidden; margin: 0;"' will be replaced by:

.invisibleStuff { position: absolute; height: 1px; width: 1px; clip: rect(1px, 1px, 1px, 1px); overflow: hidden; margin: 0; }

While we're at it, we'll remove other styling elements like the <b> tags. And since we're cleaning up the code, we'll add the new HTML5 attribute required to the elements which are required. Use native HTML whenever possible. Right now, our own Javascrip form validation function will prevent unfilled required fields from being submitted, but if Javascript is turned off, then this will prevent the form from being submitted...for the browsers that support the required attribute.

<form method="POST" action="#" name="theForm" id="theForm">
<div>Fields marked with <span class="red">*</span> are required.</div>
<label for="username">Username: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="text" name="uname" id="username" required>
<br>
<label for="firstname">Full Name: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="text" name="fname" id="firstname" placeholder="Full names must consist only of letters, a dash, and no more than 3 spaces." required>
<br>
<label for="password1">Password: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="password" name="password" id="password1" required>
<div tabindex="0">Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<label for="password2">Re-type Password: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="password" name="repassword" id="password2" required>
<fieldset>
<legend>What's your gender:</legend>
<br>
<label>
<input type="radio" name="gender" value="female">
Female</label>
<br>
<label>
<input type="radio" name="gender" value="male">
Male</label>
</fieldset>
<label for="province">Where do you live?:</label>
<select id="province" name="prov">
<option value="false">Select Province</option>
<option value="ab">Alberta:</option>
<option value="bc">British Columbia</option>
<option value="mn">Manitoba</option>
<option value="nfld">Newfoundland</option>
<option value="nb">New Brunswick</option>
<option value="nwt">Northwest Territories</option>
<option value="ns">Nova Scotia</option>
<option value="nv">Nunavit</option>
<option value="on">Ontario</option>
<option value="pei">Prince Edward Island</option>
<option value="qc">Quebec</option>
<option value="sk">Saskatchewan</option>
<option value="yk">Yukon</option>
</select>
<br>
<label for="comments">Comments:</label>
<textarea id="comments" name="comments">Enter comments here.</textarea>
<br>
<label>
<input type="checkbox" name="agree" required> I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them. <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<br>
<input type="submit" onclick="return validateBetterThenSubmit();" value="Submit">
</form>

See this example in action.


Extraneous Text

In the textarea there is some text "Enter comments here." There's also some in the Full Name field. That's called 'placeholder text', as is the text in the placeholder attribute. The problem with the placeholder attribute is that the text only appears when the user hasn't entered any data. So, if they forget what they're supposed to be entering, then they have to delete everything to see the instructions, then retype it in. Also, the default style of a lot of browsers makes it a light grey, giving much too low of a contrast ratio.

<form method="POST" action="#" name="theForm" id="theForm">
<div>Fields marked with <span class="red">*</span> are required.</div>
<label for="username">Username: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="text" name="uname" id="username" required>
<br>
<label for="firstname">Full Name: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="text" name="fname" id="firstname" title="Full names must consist only of letters, a dash, and no more than 3 spaces." required>
<br>
<label for="password1">Password: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="password" name="password" id="password1" required>
<div tabindex="0">Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<label for="password2">Re-type Password: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="password" name="repassword" id="password2" required>
<fieldset>
<legend>What's your gender:</legend>
<br>
<label>
<input type="radio" name="gender" value="female">
Female</label>
<br>
<label>
<input type="radio" name="gender" value="male">
Male</label>
</fieldset>
<label for="province">Where do you live?:</label>
<select id="province" name="prov">
<option value="false">Select Province</option>
<option value="ab">Alberta:</option>
<option value="bc">British Columbia</option>
<option value="mn">Manitoba</option>
<option value="nfld">Newfoundland</option>
<option value="nb">New Brunswick</option>
<option value="nwt">Northwest Territories</option>
<option value="ns">Nova Scotia</option>
<option value="nv">Nunavit</option>
<option value="on">Ontario</option>
<option value="pei">Prince Edward Island</option>
<option value="qc">Quebec</option>
<option value="sk">Saskatchewan</option>
<option value="yk">Yukon</option>
</select>
<br>
<label for="comments">Comments:</label>
<textarea id="comments" name="comments" title="Enter comments here."></textarea>
<br>
<label>
<input type="checkbox" name="agree" required> I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them. <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<br>
<input type="submit" onclick="return validateBetterThenSubmit();" value="Submit">
</form>

See this example in action.


The title Attribute

You'll see in the previous example I replaced the placeholder attribute in the Full Name field with a title attribute. Now that bit of text will only be seen when a user hovers over that textbox. Keyboard-only visual users, and mobile will never see it. Screen reader users may or may not hear it, depending on the screen reader, and settings. The title attribute is best left to webcomics like XKCD and Amazing Super Powers to make humourous mouse-overs.

So, to fix the Full Name field, we'll just take that text right out and leave it as part of an error message later on. Most people won't have to be told that a name consists of letters, and dahses. And very few people will have more than 4 names to enter.

<form method="POST" action="#" name="theForm" id="theForm">
<div>Fields marked with <span class="red">*</span> are required.</div>
<label for="username">Username: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="text" name="uname" id="username" required>
<br>
<label for="firstname">Full Name: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="text" name="fname" id="firstname" required>
<br>
<label for="password1">Password: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="password" name="password" id="password1" required>
<div tabindex="0">Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<label for="password2">Re-type Password: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="password" name="repassword" id="password2" required>
<fieldset>
<legend>What's your gender:</legend>
<br>
<label>
<input type="radio" name="gender" value="female">
Female</label>
<br>
<label>
<input type="radio" name="gender" value="male">
Male</label>
</fieldset>
<label for="province">Where do you live?:</label>
<select id="province" name="prov">
<option value="false">Select Province</option>
<option value="ab">Alberta:</option>
<option value="bc">British Columbia</option>
<option value="mn">Manitoba</option>
<option value="nfld">Newfoundland</option>
<option value="nb">New Brunswick</option>
<option value="nwt">Northwest Territories</option>
<option value="ns">Nova Scotia</option>
<option value="nv">Nunavit</option>
<option value="on">Ontario</option>
<option value="pei">Prince Edward Island</option>
<option value="qc">Quebec</option>
<option value="sk">Saskatchewan</option>
<option value="yk">Yukon</option>
</select>
<br>
<label for="comments">Comments:</label>
<textarea id="comments" name="comments"></textarea>
<br>
<label>
<input type="checkbox" name="agree" required> I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them. <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<br>
<input type="submit" onclick="return validateBetterThenSubmit();" value="Submit">
</form>

See this example in action.


What about ARIA?

ARIA stands for Accessible Rich Internet Applications. It's basically a set of HTML attributes to be used specifically for adaptive technologies like screen readers. Since support of ARIA can be sketchy at times, if there's a way to accomplish something using native HTML, use the HTML and not the ARIA. In time many AIRA attributes will be replaced by native HTML. Don't use aria-labelledby when you can just use a <label>.

So remember I said there was a more advanced way to make the password requirements readable by a screen reader? ARIA is that more advanced method. We'll give that section an id and in the password field(s) we'll use aria-describedby.

Note: As of this OS X 10.9.5, VoiceOver only reads the aria-describedby after a 5 second delay after some further information about the form control. Some browser and screen reader combos require the aria-describedby to have tabindex="-1". I did some of my own tests, or More information.

<form method="POST" action="#" name="theForm" id="theForm">
<div>Fields marked with <span class="red">*</span> are required.</div>
<label for="username">Username: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="text" name="uname" id="username" required>
<br>
<label for="firstname">Full Name: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="text" name="fname" id="firstname" required>
<br>
<label for="password1">Password: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="password" name="password" id="password1" required aria-describedby="passwordReq">
<div tabindex="0" id="passwordReq">Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<label for="password2">Re-type Password: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="password" name="repassword" id="password2" required aria-describedby="passwordReq">
<fieldset>
<legend>What's your gender:</legend>
<br>
<label>
<input type="radio" name="gender" value="female">
Female</label>
<br>
<label>
<input type="radio" name="gender" value="male">
Male</label>
</fieldset>
<label for="province">Where do you live?:</label>
<select id="province" name="prov">
<option value="false">Select Province</option>
<option value="ab">Alberta:</option>
<option value="bc">British Columbia</option>
<option value="mn">Manitoba</option>
<option value="nfld">Newfoundland</option>
<option value="nb">New Brunswick</option>
<option value="nwt">Northwest Territories</option>
<option value="ns">Nova Scotia</option>
<option value="nv">Nunavit</option>
<option value="on">Ontario</option>
<option value="pei">Prince Edward Island</option>
<option value="qc">Quebec</option>
<option value="sk">Saskatchewan</option>
<option value="yk">Yukon</option>
</select>
<br>
<label for="comments">Comments:</label>
<textarea id="comments" name="comments"></textarea>
<br>
<label>
<input type="checkbox" name="agree" required> I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them. <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<br>
<input type="submit" onclick="return validateBetterThenSubmit();" value="Submit">
</form>

See this example in action.

Notice I left the tabindex="0" in there? I could have taken it out, but what if someone's using a screen reader and browser combo that doesn't do well with aria? This way they get it for sure. It will be read with each password box and in between them in the tab order. (I can use Javascript to remove the tabindex="0" upon page load if I want.)


Don't forget the errors!

Inevitably a user will put bad data into a form, and those errors need to be caught and reported to the user. And the user may need some help to fix the error.

Your form data should be validated at the server for sure. If you want to validate in the browser with Javascript, that's fine too. You need to validate at the server for security purposes. If the submitted form has invalid data, then the user should be sent back to the page where errors are listed in a list of links to the fields that contain the errors. As for accessibility, there's nothing wrong with this approach, but when the page re-loads, the focus will be at the top of the page and a blind user will have to go through the page until they get to the errors so they can fix them and re-submit. The only accessibility consideration here is that you should probably add the word "Error" to the page title so a blind user will hear that they're on the same page, but there are errors to be fixed.

For the remainder of the tutorial we will assume client-side Javascript form validation. We'll assume those functions are already written and we don't need to see how they're done. We'll assume the validate() returns an object with a list of errors, and the id values of the form controls to which they refer.


Reporting Errors

In the form we've been using thus far, let's assume the user entered a bad password; one that did not conform, a bad Full name, and they did not retype their password at all.

So far in this tutorial some Javascript validation has been happening. But the whole time, if there are errors you have no way of knowing! The form just sits there like a lump on a log making you wonder why the form didn't submit. You need to report those errors!

Now we'll introduce some basic error reporting. In the example, click the "Submit" button to see the error messages.

<form method="POST" action="#" name="theForm" id="theForm">
<div>Fields marked with <span class="red">*</span> are required.</div>
<label for="username">Username: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="text" name="uname" id="username" value="myusername" required>
<br>
<label for="fullname">Full Name: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="text" name="fname" id="fullname" value="B0B Sm1th" required>
<br>
<label for="password1">Password: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="password" name="password" id="password1" required aria-describedby="passwordReq" value="x">
<div tabindex="0" id="passwordReq">Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<label for="password2">Re-type Password: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="password" name="repassword" id="password2" required aria-describedby="passwordReq" value="">
<fieldset>
<legend>What's your gender:</legend>
<br>
<label>
<input type="radio" name="gender" value="female">
Female</label>
<br>
<label>
<input type="radio" name="gender" value="male">
Male</label>
</fieldset>
<label for="province">Where do you live?:</label>
<select id="province" name="prov">
<option value="false">Select Province</option>
<option value="ab">Alberta:</option>
<option value="bc">British Columbia</option>
<option value="mn">Manitoba</option>
<option value="nfld">Newfoundland</option>
<option value="nb">New Brunswick</option>
<option value="nwt">Northwest Territories</option>
<option value="ns">Nova Scotia</option>
<option value="nv">Nunavit</option>
<option value="on">Ontario</option>
<option value="pei">Prince Edward Island</option>
<option value="qc">Quebec</option>
<option value="sk">Saskatchewan</option>
<option value="yk">Yukon</option>
</select>
<br>
<label for="comments">Comments:</label>
<textarea id="comments" name="comments"></textarea>
<br>
<label>
<input type="checkbox" name="agree" checked required> I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them. <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<br>
<div id="errorBoxOuter" style="display: none; position:absolute; left: 0; top: 0; padding: 0; margin: 0; height: 100%; width: 100%; background-color: rgba(33, 33, 33, 0.8); z-index: 1;">
<div style="background-color: #FFFFFF; border: thin solid black; position: relative; margin: 5em auto; top: 30px; height: 10em; width: 50%;padding:3em;">
Errors:
<ol>
<li>Bad Full name. Letters and spaces only, please.</li>
<li>Bad password</li>
<li>You forgot to re-type your password.</li>
</ol>
<div>
<input type="button" value="Okay" id="okayBtn" onclick='document.getElementById("errorBoxOuter").style.display = "none";'>
</div>
</div>
</div>
<input type="submit" onclick='document.getElementById("errorBoxOuter").style.display = "block"; return false;' value="Submit">
</form>

See this example in action.


Reporting Errors on the Page

In the previous example we saw the errors reported in a dialog box. Dialog boxes take a bit of work to make accessible. Even if the dialog box is accessible (which the example wasn't!), once you close the box the list of errors is gone! You have to remember what the problems were. This is a problem for people with some cognitive impairments. We'll fix that here.

Instead of a dialog box that disappears, we'll add a <div> at the top with a list of the errors.

Don't forget to throw the focus to it so someone with a screen reader will know what's going on.

<form method="POST" action="#" name="theForm" id="theForm">
<div id="errorBox" style="background-color: #FFFFFF; color: #AA0000; border: thin solid #AA0000;width: 50%;padding:1.5em;" tabindex="0">
Errors:
<ol>
<li>Bad Full name. Letters and spaces only, please.</li>
<li>Bad password</li>
<li>You forgot to re-type your password.</li>
</ol>
<div>
<input type="button" value="Okay" id="okayBtn" onclick='document.getElementById("errorBoxOuter").style.display = "none";'>
</div>
</div>

<div>Fields marked with <span class="red">*</span> are required.</div>
<label for="username">Username: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="text" name="uname" id="username" value="myusername" required>
<br>
<label for="fullname">Full Name: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="text" name="fname" id="fullname" value="B0B Sm1th" required>
<br>
<label for="password1">Password: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="password" name="password" id="password1" required aria-describedby="passwordReq" value="x">
<div tabindex="0" id="passwordReq">Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<label for="password2">Re-type Password: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="password" name="repassword" id="password2" required aria-describedby="passwordReq" value="">
<fieldset>
<legend>What's your gender:</legend>
<br>
<label>
<input type="radio" name="gender" value="female">
Female</label>
<br>
<label>
<input type="radio" name="gender" value="male">
Male</label>
</fieldset>
<label for="province">Where do you live?:</label>
<select id="province" name="prov">
<option value="false">Select Province</option>
<option value="ab">Alberta:</option>
<option value="bc">British Columbia</option>
<option value="mn">Manitoba</option>
<option value="nfld">Newfoundland</option>
<option value="nb">New Brunswick</option>
<option value="nwt">Northwest Territories</option>
<option value="ns">Nova Scotia</option>
<option value="nv">Nunavit</option>
<option value="on">Ontario</option>
<option value="pei">Prince Edward Island</option>
<option value="qc">Quebec</option>
<option value="sk">Saskatchewan</option>
<option value="yk">Yukon</option>
</select>
<br>
<label for="comments">Comments:</label>
<textarea id="comments" name="comments"></textarea>
<br>
<label>
<input type="checkbox" name="agree" checked required> I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them. <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<br>
<input type="submit" onclick='document.getElementById("errorBox").focus(); return false; ' value="Submit">
</form>

See this example in action.


Reporting Errors with Each Form Control

Another "best practice" would be to put each error with each form control.

<form method="POST" action="#" name="theForm" id="theForm">
<div id="errorBox" style="background-color: #FFFFFF; color: #AA0000; border: thin solid #AA0000; width: 50%;padding:1.5em;" tabindex="0">
Errors:
<ol>
<li><a href="#fullname">Bad Full name. Letters and spaces only, please.</a></li>
<li><a href="#password1">Bad password</a></li>
<li><a href="#password2">You forgot to re-type your password.</a></li>
</ol>
<div>
<input type="button" value="Okay" id="okayBtn" onclick='document.getElementById("errorBoxOuter").style.display = "none";'>
</div>
</div>

<div>Fields marked with <span class="red">*</span> are required.</div>
<label for="username">Username: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="text" name="uname" id="username" value="myusername" required>
<br>
<div class="red">Bad Full name. Letters and spaces only, please.</div>
<label for="fullname">Full Name: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="text" name="fname" id="fullname" value="B0B Sm1th" required>
<br>
<div class="red">Bad password</div>
<label for="password1">Password: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="password" name="password" id="password1" required aria-describedby="passwordReq" value="x">
<div tabindex="0" id="passwordReq">Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<div class="red">You forgot to re-type your password.</div>
<label for="password2">Re-type Password: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="password" name="repassword" id="password2" required aria-describedby="passwordReq" value="">
<fieldset>
<legend>What's your gender:</legend>
<br>
<label>
<input type="radio" name="gender" value="female">
Female</label>
<br>
<label>
<input type="radio" name="gender" value="male">
Male</label>
</fieldset>
<label for="province">Where do you live?:</label>
<select id="province" name="prov">
<option value="false">Select Province</option>
<option value="ab">Alberta:</option>
<option value="bc">British Columbia</option>
<option value="mn">Manitoba</option>
<option value="nfld">Newfoundland</option>
<option value="nb">New Brunswick</option>
<option value="nwt">Northwest Territories</option>
<option value="ns">Nova Scotia</option>
<option value="nv">Nunavit</option>
<option value="on">Ontario</option>
<option value="pei">Prince Edward Island</option>
<option value="qc">Quebec</option>
<option value="sk">Saskatchewan</option>
<option value="yk">Yukon</option>
</select>
<br>
<label for="comments">Comments:</label>
<textarea id="comments" name="comments"></textarea>
<br>
<label>
<input type="checkbox" name="agree" checked required> I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them. <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<br>
<input type="submit" onclick='document.getElementById("errorBox").focus(); return false; ' value="Submit">
</form>

See this example in action.


Reporting Errors in the Label

By now you should see what's wrong with the previous example. A screen reader in "forms mode" will miss all the error messages that appear before the form controls. The best thing to do is append the erros to the <label> text.

<form method="POST" action="#" name="theForm" id="theForm">
<div id="errorBox" style="background-color: #FFFFFF; color: #AA0000; border: thin solid #AA0000; width: 50%;padding:1.5em;" tabindex="0">
Errors:
<ol>
<li><a href="#fullname">Bad Full name. Letters and spaces only, please.</a></li>
<li><a href="#password1">Bad password</a></li>
<li><a href="#password2">You forgot to re-type your password.</a></li>
</ol>
<div>
<input type="button" value="Okay" id="okayBtn" onclick='document.getElementById("errorBoxOuter").style.display = "none";'>
</div>
</div>

<div>Fields marked with <span class="red">*</span> are required.</div>
<label for="username">Username: <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<input type="text" name="uname" id="username" value="myusername" required>
<br>
<label for="fullname">Full Name: <span class="red">*</span> <span class="invisibleStuff">(Required)</span><div class="red">Bad Full name. Letters and spaces only, please.</div></label>
<input type="text" name="fname" id="fullname" value="B0B Sm1th" required>
<br>
<label for="password1">Password: <span class="red">*</span> <span class="invisibleStuff">(Required)</span><div class="red">Bad password</div></label>
<input type="password" name="password" id="password1" required aria-describedby="passwordReq" value="x">
<div tabindex="0" id="passwordReq">Passwords should be at least 8 characters, contain numbers, upper and lower case letters and a special character. This way it will be hard for you to remember, but easy for a computer to guess.</div>
<label for="password2">Re-type Password: <span class="red">*</span> <span class="invisibleStuff">(Required)</span><div class="red">You forgot to re-type your password.</div></label>
<input type="password" name="repassword" id="password2" required aria-describedby="passwordReq" value="">
<fieldset>
<legend>What's your gender:</legend>
<br>
<label>
<input type="radio" name="gender" value="female">
Female</label>
<br>
<label>
<input type="radio" name="gender" value="male">
Male</label>
</fieldset>
<label for="province">Where do you live?:</label>
<select id="province" name="prov">
<option value="false">Select Province</option>
<option value="ab">Alberta:</option>
<option value="bc">British Columbia</option>
<option value="mn">Manitoba</option>
<option value="nfld">Newfoundland</option>
<option value="nb">New Brunswick</option>
<option value="nwt">Northwest Territories</option>
<option value="ns">Nova Scotia</option>
<option value="nv">Nunavit</option>
<option value="on">Ontario</option>
<option value="pei">Prince Edward Island</option>
<option value="qc">Quebec</option>
<option value="sk">Saskatchewan</option>
<option value="yk">Yukon</option>
</select>
<br>
<label for="comments">Comments:</label>
<textarea id="comments" name="comments"></textarea>
<br>
<label>
<input type="checkbox" name="agree" checked required> I have read the <a href="accessibleForms/tandc.php">Terms and Conditions</a> and I agree to comply with them. <span class="red">*</span> <span class="invisibleStuff">(Required)</span></label>
<br>
<input type="submit" onclick='document.getElementById("errorBox").focus(); return false; ' value="Submit">
</form>

See this example in action.


The TL;DR Conclusion

That was a long tutorial, eh? It's only long because I've seen so many forms built by developers that, I swear have gone out of their way to discover new ways to make forms inaccessible.

Don't be one of those developers. Be a good one. Please. You'll thank yourself when your eyes start going when you get older, or when you sustain an injury (however temporary) or lose your mobility.

In short: keep to the following rules when developing web forms (or any web page components):

  1. Use HTML the way HTML was meant to be used. (See and bookmark HTML5 Elements for details.)
  2. Make sure everything can be done using a keyboard, either intuitively or include instructions.
  3. Once a form begins, make sure all elements will either be focusable (by either using a form element, a hyperlink, or using tabindex="0" on non-form elements), or using elements that will be read by screen readers (like <label>s, <fieldset/legend>s, or <aria-labelledby/aria-describedby>).
  4. To be safe, put all information about the form above the form.
  5. List error messages as links to the input fields above the form, and repeat each error message within the form <label>/<legend>.
  6. Bonus points for implementing the Government of Canada's initiative, the Web Experience Toolkit.