Forms, Form Validation, and CAPTCHAs
On the web, forms are constantly being used to collect information from users: signing up for something, buying something, asking a question, or contacting someone. Since forms are so commonly used, it is imperative to make forms accessible for all users.
Best Practices
These best practices have used substantial information from the US design standards guide.
Form Layout
A set of
input
fields within a form should be grouped byfieldset
elements if they are related.Do not use auto-advance in your form. (This is terrible for people with disabilities – don’t do it)!
Do not rearrange the order of the form, or inputs, with CSS. (This can cause extreme confusion for screen readers, and their users).
Ideally, present only one or two inputs per line for users with limited vision who have trouble scanning the screen horizontally.
Ensure that your color/contrast ratios and typography are accessible.
Each
fieldset
should have alegend
element (directly under the firstfieldset
tag) that describes the grouping.These practices give screen readers and users of assistive technology more information about the form, allowing them to better fill it out.
Every
input
element in aform
should have a relatedlabel
. You can either have a hybridlabel
, which is recommended for accessibility, or an explicitlabel
.- Hybrid labels: These are labels which surround their associated input.
- Explicit labels: These are labels which precede or are adjacent to their corresponding input.
Your form submission button should have
type="submit"
and have, like all buttons, a clear, actionable title. Do not use auto-submission after the user has filled out the last input.- Example: “Send Message”, “Submit Message”, “Subscribe”
Form Inputs
This information applies to all sorts of valid inputs: text inputs, textarea
elements, telephone numbers, address entry, select
elements, checkboxes, radio buttons, and date inputs (we have specifics on some of these types of inputs below).
All form inputs should have a related, unique
label
that either wraps it, or precedes it. The labelfor
attribute should match its input’sid
element, regardless of whether it wraps the element or not.Try to give all inputs a
type=''
attribute (types include email, tel, text, submit, etc.). When supported by a browser, they improve the experience of all users.If the input is required, put “Required” or “(required)” within a
span
in the label. Consider not using “*” to indicate required inputs as it is often not read by screen readers.- Example:
<label for="name">First Name <span>(required)</span></label>
- Also, within the
input
, include an HTML5required=''
attribute so that you can use JavaScript form validation. (If you use both the HTML5required
andaria-required
attributes in theinput
element, a screen reader will end up reading “required” twice, which is a bit annoying). - Also, for required inputs, you can include regular expression pattern information, or even
max-length
of characters in the input attributes, which many screen readers can understand.
- Example:
Do not use placeholder text. Placeholder text poses a variety of accessibility issues (including possible problems with color/contrast, users thinking the form input is already filled out, and placeholder text replacing a form label).
- Give all the information about what is required to fill out the input in the form label, not the placeholder.
If you have required inputs, use JavaScript to visually and physically align validation messages with labels for certain input fields (that way, a screen reader will read out the error while reading over the label).
Inputs requiring numbers
Try not to break numbers into distinct inputs (such as phone numbers, Social Security Numbers, or credit card numbers).
- The US Design Standards advise: “For example, use one input for [each] phone number, not three (not one for area code, one for local code, and one for number). Each field needs to be labeled for a screen reader and the labels for fields broken into segments are often not meaningful.”
Checkboxes, Radio Buttons
All related checkboxes should be surrounded by unordered list and a
fieldset
with alegend
tag. The same goes for a set of related radio buttons. However, for a single checkbox, or single radio button, do use afieldset
andlegend
.Since the only label associated with a checkbox or radio button is the option itself - e.g. ‘Yes,’ 'No,’ 'Banana,’ it is important to associate the option with a legend
id
(the legend could be: 'Would you like to be on our mailing list?’). Place anaria-labelledby='{id of legend element}'
on the label or the unordered list surrounding a set of related checkboxes/radio buttons. Also, place an ariarole='radiogroup'
orrole='group'
on the surrounding element as well. View our code snippets below for further details.
Select inputs
- Selects are by far the most cognitively and technically challenging input. Consider using another input, such as a checkbox or radio button instead.
Date input
- The US Design Standards states that for an input which required a month, day, and year, “Three text fields are the easiest way for users to enter most dates.”
Code Snippets
Below we have supplied the code for a variety of accessible form inputs. There are many ways to use jQuery/JavaScript to make your forms accessible, and through testing and research, we have found our own unique solution below.
Our solution takes advantage of the jQuery .validity.valid
, and applying an aria-live='assertive'
attribute to the first new error message on the form. (The error message must be dynamically added for this aria-live message to be read). Also, we have found that if there are multiple aria-live regions, a screen reader will only read the last one on the page, so there should really only be one aria-live region ever on a page.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | <form class="form-example" method="post"> <fieldset> <legend>Personal Information</legend> <label for="full-name"> Full Name <span class="required"> (required) </span> <span class="error-message"> You must input a real name. </span> <input id="full-name" name="full-name" required="" type="text"> </label> <label for="email"> Email <span class="required"> (required) </span> <span class="error-message"> The email address you entered is not valid. </span> <input id="email" name="email" required="" type="email"> </label> </fieldset> <fieldset> <legend>Credit Card Information</legend> <label for="cc"> Credit Card Number <span class="required"> (required) </span> <span class="error-message"> Your credit card number should be all numbers. </span> <input id="cc" name="cc" required="" type="text" minlength='13' maxlength='20' pattern='\s*[0-9]+-?\s*[0-9]+-?\s*[0-9]+-?\s*[0-9]+-?\s*'> </label> </fieldset> <fieldset> <label for='text-comments'> Please leave your comments or questions here <span class="required"> (required) </span> <span class="error-message"> Please give us a little more information about your inquiry. </span> <textarea id='text-comments' required=''></textarea> </label> </fieldset> <fieldset> <legend id='legend-1'>Can we add you to our email mailing list? <span class="required">(required)</span></legend> <ul aria-labelledby='legend-1' role='radiogroup'> <li> <label for="yes"> <span class="error-message"> Please select yes or no. </span> <input id="yes" required="" type="radio" name="answers" value="yes"> Yes </label> </li> <li> <label for="no"> <input id="no" type="radio" name="answers" value="no"> No </label> </li> </ul> </fieldset> <fieldset> <legend id='legend-2'>Which topics interest you? <span class="required">(required)</span></legend> <ul aria-labelledby='legend-2' role='group'> <li> <label for="science"> <span class="error-message"> Please select at least one category. </span> <input id="science" required="" type="checkbox" name="categories" value="science"> Science </label> </li> <li> <label for="children-programs"> <input id="children-programs" required="" type="checkbox" name="categories" value="children-programs"> Programs for Children </label> </li> <li> <label for="new-events"> <input id="new-events" required="" type="checkbox" name="categories" value="new-events"> New Events </label> </li> </ul> </fieldset> <fieldset> <label for="options">Choose from the following: <span class="required"> (required) </span> <span class="error-message"> Please choose one of the following options. </span> <select name="options" id="options" required=''> <option value=''> Select </option> <option value="value1">Option A</option> <option value="value2">Option B</option> <option value="value3">Option C</option> </select> </label> </fieldset> <button class='form-example-submit' type="submit">Submit Form</button> </form> |
1 2 3 4 5 6 7 | input.edited:invalid { box-shadow: 0 0 5px 1px red; } input:focus:invalid { outline: none; } |
JavaScript validation with jQuery:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | function handleFormSubmission(){ $('.form-example input').on('keyup', function(e){ $(this).addClass('edited'); }); $('.form-example [type="submit"]').on('click.formValidation', function(e){ shouldPrevent = false; errorList = []; $form = $(this).parents('form'); $inputs = $form.find('input[required], textarea[required], select[required]'); $inputs.each(function(index, input){ errorMessageSelector = 'label[for="' + $(input).attr('id') + '"] .error-message'; $form.find(errorMessageSelector).removeAttr('aria-live'); error = $form.find(errorMessageSelector); error.css('display', 'none'); if(!input.validity.valid){ error.css('display', 'inline-block'); shouldPrevent = true; errorList.push(error); } else { error.css('display', 'none'); } }); if(!$form[0].checkValidity()){ e.preventDefault(); errorList[0].attr('aria-live', 'assertive'); } }); } $(document).ready(function(){ handleFormSubmission(); }); |
Live Example
CAPTCHAs
CAPTCHA forms are implemented to stop spammers and bots from submitting forms, but in doing so, they also deny some human users access.
CAPTCHAs are not blind-friendly, and are difficult for users with learning disabilities like dyslexia.
Consider an alternative method:
Logic questions: Use a question rather than an image or audio. A sample CAPTCHA question might be, “Which animal is larger, a mouse or a horse?” or “What state is Philadelphia located in?” You can also ask math questions, such as, “What is one plus three?” For high-volume sites, rotate questions, as hackers can develop algorithms to predict questions and answers or hire users to answer CAPTCHA questions. However, user with cognitive disabilities may still have trouble with this approach.
Moderators: For services with relatively light traffic, you can manually approve access or postings.
Spam detection: You could use natural language filters to detect spam messages, or watch for activity coming from similar IP addresses. Most major blogging or content management software contains spam filtering capabilities, or can be fitted with a plug-in for this functionality. Many of these filters can automatically delete messages that reach a certain spam threshold, and mark questionable messages for manual moderation.
Phone, text, or email verification: Email verification, where you acknowledge receipt of an email, is a common authentication technique that can be very accessible. Another way is telephone or SMS authentication.
Tools & Resources
Jim Allan’s Recommended Resources to Making More Accessible Forms
Text CAPTCHA generates textual CAPTCHAs based on simple logic questions