Form Design Patterns Book Excerpt: A Registration Form
Form Design Patterns Book Excerpt: A Registration Form
Adam Silver2018-10-10T12:25:00+02:002018-10-10T12:07:58+00:00
Let’s start with a registration form. Most companies want long-term relationships with their users. To do that they need users to sign up. And to do that, they need to give users value in return. Nobody wants to sign up to your service — they just want to access whatever it is you offer, or the promise of a faster experience next time they visit.
Despite the registration form’s basic appearance, there are many things to consider: the primitive elements that make up a form (labels, buttons, and inputs), ways to reduce effort (even on small forms like this), all the way through to form validation.
In choosing such a simple form, we can zoom in on the foundational qualities found in well-designed forms.
How It Might Look
The form is made up of four fields and a submit button. Each field is made up of a control (the input) and its associated label.
Here’s the HTML:
<form>
<label for="firstName">First name</label>
<input type="text" id="firstName" name="firstName">
<label for="lastName">Last name</label>
<input type="text" id="lastName" name="lastName">
<label for="email">Email address</label>
<input type="email" id="email" name="email">
<label for="password">Create password</label>
<input type="password" id="password" name="password" placeholder="Must be at least 8 characters">
<input type="submit" value="Register">
</form>
Labels are where our discussion begins.
Labels
In Accessibility For Everyone, Laura Kalbag sets out four broad parameters that improve the user experience for everyone:
- Visual: make it easy to see.
- Auditory: make it easy to hear.
- Motor: make it easy to interact with.
- Cognitive: make it easy to understand.
By looking at labels from each of these standpoints, we can see just how important labels are. Sighted users can read them, visually-impaired users can hear them by using a screen reader, and motor-impaired users can more easily set focus to the field thanks to the larger hit area. That’s because clicking a label sets focus to the associated form element.
For these reasons, every control that accepts input should have an auxiliary . Submit buttons don’t accept input, so they don’t need an auxiliary label — the
value
attribute, which renders the text inside the button, acts as the accessible label.
To connect an input to a label, the input’s id
and label’s for
attribute should match and be unique to the page. In the case of the email field, the value is “email”:
html
<label for="email">Email address</label>
<input id="email">
Failing to include a label means ignoring the needs of many users, including those with physical and cognitive impairments. By focusing on the recognized barriers to people with disabilities, we can make our forms easier and more robust for everyone.
For example, a larger hit area is crucial for motor-impaired users, but is easier to hit for those without impairments too.
Placeholders
The placeholder
attribute is intended to store a hint. It gives users extra guidance when filling out a field — particularly useful for fields that have complex rules such as a password field.
As placeholder text is not a real value, it’s grayed out so that it can be differentiated from user-entered values.
Unlike labels, hints are optional and shouldn’t be used as a matter of course. Just because the placeholder
attribute exists doesn’t mean we have to use it. You don’t need a placeholder of “Enter your first name” when the label is “First name” — that’s needless duplication.
Placeholders are appealing because of their minimal, space-saving aesthetic. This is because placeholder text is placed inside the field. But this is a problematic way to give users a hint.
First, they disappear when the user types. Disappearing text is hard to remember, which can cause errors if, for example, the user forgets to satisfy one of the password rules. Users often mistake placeholder text for a value, causing the field to be skipped, which again would cause errors later on. Gray-on-white text lacks sufficient contrast, making it generally hard-to-read. And to top it off, some browsers don’t support placeholders, some screen readers don’t announce them, and long hint text may get cut off.
That’s a lot of problems for what is essentially just text. All content, especially a form hint, shouldn’t be considered as nice to have. So instead of using placeholders, it’s better to position hint text above the control like this:
<div class="field">
<label for="password">
<span class="field-label">Password</span>
<span class="field-hint">Must contain 8+ characters with at least 1 number and 1 uppercase letter.</span>
</label>
<input type="password" id="password" name="password">
</div>
The hint is placed within the label and inside a so it can be styled differently. By placing it inside the label it will be read out by screen readers, and further enlarges the hit area.
As with most things in design, this isn’t the only way to achieve this functionality. We could use ARIA attributes to associate the hint with the input:
<div class="field">
<label for="password">Password</label>
<p class="field-hint" id="passwordhint">Must contain 8+ characters with at least 1 number and 1 uppercase letter.</p>
<input type="password" id="password" name="password" aria-describedby="passwordhint">
</div>
The aria-describedby
attribute is used to connect the hint by its id
— just like the for attribute for labels, but in reverse. It’s appended to the control’s label and read out after a short pause. In this example, “password [pause] must contain eight plus characters with at least one number and one uppercase letter.”
There are other differences too. First, clicking the hint (a
in this case) won’t focus the control, which reduces the hit area. Second, despite ARIA’s growing support, it’s never going to be as well supported as native elements. In this particular case, Internet Explorer 11 doesn’t support aria-describedby
. This is why the first rule of ARIA is not to use ARIA:
“If you can use a native HTML element or attribute with the semantics and behaviour you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.”
Float Labels
The float label pattern by Matt Smith is a technique that uses the label as a placeholder. The label starts inside the control, but floats above the control as the user types, hence the name. This technique is often lauded for its quirky, minimalist, and space-saving qualities.
Unfortunately, there are several problems with this approach. First, there is no space for a hint because the label and hint are one and the same. Second, they’re hard to read, due to their poor contrast and small text, as they’re typically designed. (Lower contrast is necessary so that users have a chance to differentiate between a real value and a placeholder.) Third, like placeholders, they may be mistaken for a value and could get cropped.
And float labels don’t actually save space. The label needs space to move into in the first place. Even if they did save space, that’s hardly a good reason to diminish the usability of forms.
“Seems like a lot of effort when you could simply put labels above inputs & get all the benefits/none of the issues.”
— Luke Wroblewski on float labels
Quirky and minimalist interfaces don’t make users feel awesome — obvious, inclusive, and robust interfaces do. Artificially reducing the height of forms like this is both uncompelling and problematic.
Instead, you should prioritize making room for an ever-present, readily available label (and hint if necessary) at the start of the design process. This way you won’t have to squeeze content into a small space.
We’ll be discussing several, less artificial techniques to reduce the size of forms shortly.
The Question Protocol
One powerful and natural way to reduce the size of a form is to use a question protocol. It helps ensure you know why you are asking every question or including a form field.
Does the registration form need to collect first name, last name, email address and password? Are there better or alternative ways to ask for this information that simplify the experience?
In all likelihood, you don’t need to ask for the user’s first and last name for them to register. If you need that information later, for whatever reason, ask for it then. By removing these fields, we can halve the size of the form. All without resorting to novel and problematic patterns.
No Password Sign-In
One way to avoid asking users for a password is to use the no password sign-in pattern. It works by making use of the security of email (which already needs a password). Users enter only their email address, and the service sends a special link to their inbox. Following it logs the user into the service immediately.
Not only does this reduce the size of the form to just one field, but it also saves users having to remember another password. While this simplifies the form in isolation, in other ways it adds some extra complexity for the user.
First, users might be less familiar with this approach, and many people are worried about online security. Second, having to move away from the app to your email account is long-winded, especially for users who know their password, or use a password manager.
It’s not that one technique is always better than the other. It’s that a question protocol urges us to think about this as part of the design process. Otherwise, you’d mindlessly add a password field on the form and be done with it.
Passphrases
Passwords are generally short, hard to remember, and easy to crack. Users often have to create a password of more than eight characters, made up of at least one uppercase and one lowercase letter, and a number. This micro-interaction is hardly ideal.
“Sorry but your password must contain an uppercase letter, a number, a haiku, a gang sign, a hieroglyph, and the blood of a virgin.”
— Anonymous internet meme
Instead of a password, we could ask users for a passphrase. A passphrase is a series of words such as “monkeysinmygarden” (sorry, that’s the first thing that comes to mind). They are generally easier to remember than passwords, and they are more secure owing to their length — passphrases must be at least 16 characters long.
The downside is that passphrases are less commonly used and, therefore, unfamiliar. This may cause anxiety for users who are already worried about online security.
Whether it’s the no password sign-in pattern or passphrases, we should only move away from convention once we’ve conducted thorough and diverse user research. You don’t want to exchange one set of problems for another unknowingly.
Field Styling
The way you style your form components will, at least in part, be determined by your product or company’s brand. Still, label position and focus styles are important considerations.
Label Position
Matteo Penzo’s eye-tracking tests showed that positioning the label above (as opposed to beside) the form control works best.
“Placing a label right over its input field permitted users to capture both elements with a single eye movement.”
But there are other reasons to put the label above the field. On small viewports there’s no room beside the control. And on large viewports, zooming in increases the chance of the text disappearing off screen.
Also, some labels contain a lot of text, which causes it to wrap onto multiple lines, which would disrupt the visual rhythm if placed next to the control.
While you should aim to keep labels terse, it’s not always possible. Using a pattern that accommodates varying content — by positioning labels above the control — is a good strategy.
Look, Size, and Space
Form fields should look like form fields. But what does that mean exactly?
It means that a text box should look like a text box. Empty boxes signify “fill me in” by virtue of being empty, like a coloring-in book. This happens to be part of the reason placeholders are unhelpful. They remove the perceived affordance an empty text box would otherwise provide.
This also means that the empty space should be boxed in (bordered). Removing the border, or having only a bottom border, for example, removes the perceived affordances. A bottom border might at first appear to be a separator. Even if you know you have to fill something in, does the value go above the line or below it?
Spatially, the label should be closest to its form control, not the previous field’s control. Things that appear close together suggest they belong together. Having equal spacing might improve aesthetics, but it would be at the cost of usability.
Finally, the label and the text box itself should be large enough to read and tap. This probably means a font size of at least 16 pixels, and ideally an overall tap target of at least 44px.
Focus Styles
Focus styles are a simpler prospect. By default, browsers put an outline around the element in focus so users, especially those who use a keyboard, know where they are. The problem with the default styling is that it is often faint and hard to see, and somewhat ugly.
While this is the case, don’t be tempted to remove it, because doing so will diminish the user experience greatly for those traversing the screen by keyboard. We can override the default styling to make it clearer and more aesthetically pleasing.
input:focus {
outline: 4px solid #ffbf47;
}
The Email Field
Despite its simple appearance there are some important details that have gone into the field’s construction which affect the experience.
As noted earlier, some fields have a hint in addition to the label, which is why the label is inside a child span. The field-label
class lets us style it through CSS.
<div class="field">
<label for="email">
<span class="field-label">Email address</span>
</label>
<input type="email" id="email" name="email">
</div>
The label itself is “Email address” and uses sentence case. In “Making a case for letter case,” John Saito explains that sentence case (as opposed to title case) is generally easier to read, friendlier, and makes it easier to spot nouns. Whether you heed this advice is up to you, but whatever style you choose, be sure to use it consistently.
The input’s type
attribute is set to email
, which triggers an email-specific onscreen keyboard on mobile devices. This gives users easy access to the @
and .
(dot) symbols which every email address must contain.
People using a non-supporting browser will see a standard text input (). This is a form of progressive enhancement, which is a cornerstone of designing inclusive experiences.
Progressive Enhancement
Progressive enhancement is about users. It just happens to make our lives as designers and developers easier too. Instead of keeping up with a set of browsers and devices (which is impossible!) we can just focus on features.
First and foremost, progressive enhancement is about always giving users a reasonable experience, no matter their browser, device, or quality of connection. When things go wrong — and they will — users won’t suffer in that they can still get things done.
There are a lot of ways an experience can go wrong. Perhaps the style sheet or script fails to load. Maybe everything loads, but the user’s browser doesn’t recognize some HTML, CSS, or JavaScript. Whatever happens, using progressive enhancement when designing experiences stops users having an especially bad time.
It starts with HTML for structure and content. If CSS or JavaScript don’t load, it’s fine because the content is there.
If everything loads OK, perhaps various HTML elements aren’t recognized. For example, some browsers don’t understand . That’s fine, though, because users will get a text box (
) instead. Users can still enter an email address; they just don’t get an email-specific keyboard on mobile.
Maybe the browser doesn’t understand some fancy CSS, and it will just ignore it. In most cases, this isn’t a problem. Let’s say you have a button with border-radius: 10px
. Browsers that don’t recognize this rule will show a button with angled corners. Arguably, the button’s perceived affordance is reduced, but users are left unharmed. In other cases it might be helpful to use feature queries.
Then there is JavaScript, which is more complicated. When the browser tries to parse methods it doesn’t recognize, it will throw a hissy fit. This can cause your other (valid and supported) scripts to fail. If your script doesn’t first check that the methods exist (feature detection) and work (feature testing) before using them, then users may get a broken interface. For example, if a button’s click handler calls a method that’s not recognized, the button won’t work. That’s bad.
That’s how you enhance. But what’s better is not needing an enhancement at all. HTML with a little CSS can give users an excellent experience. It’s the content that counts and you don’t need JavaScript for that. The more you can rely on content (HTML) and style (CSS), the better. I can’t emphasize this enough: so often, the basic experience is the best and most performant one. There’s no point in enhancing something if it doesn’t add value (see inclusive design principle 7).
Of course, there are times when the basic experience isn’t as good as it could be — that’s when it’s time to enhance. But if we follow the approach above, when a piece of CSS or JavaScript isn’t recognized or executed, things will still work.
Progressive enhancement makes us think about what happens when things fail. It allows us to build experiences with resilience baked in. But equally, it makes us think about whether an enhancement is needed at all; and if it is, how best to go about it.
The Password Field
We’re using the same markup as the email field discussed earlier. If you’re using a template language, you’ll be able to create a component that accommodates both types of field. This helps to enforce inclusive design principle 3, be consistent.
<div class="field">
<label for="password">
<span class="field-label">Choose password</span>
<span class="field-hint">Must contain 8+ characters with at least 1 number and 1 uppercase letter.</span>
</label>
<input type="password" id="password" name="password">
</div>
The password field contains a hint. Without one, users won’t understand the requirements, which is likely to cause an error once they try to proceed.
The type="password"
attribute masks the input’s value by replacing what the user types with small black dots. This is a security measure that stops people seeing what you typed if they happen to be close by.
A Password Reveal
Obscuring the value as the user types makes it hard to fix typos. So when one is made, it’s often easier to delete the whole entry and start again. This is frustrating as most users aren’t using a computer with a person looking over their shoulder.
Owing to the increased risk of typos, some registration forms include an additional “Confirm password” field. This is a precautionary measure that requires the user to type the same password twice, doubling the effort and degrading the user experience. Instead, it’s better to let users reveal their password, which speaks to principles 4 and 5, give control and offer choice respectively. This way users can choose to reveal their password when they know nobody is looking, reducing the risk of typos.
Recent versions of Internet Explorer and Microsoft Edge provide this behavior natively. As we’ll be creating our own solution, we should suppress this feature using CSS like this:
input[type=password]::-ms-reveal {
display: none;
}
First, we need to inject a button next to the input. The element should be your go-to element for changing anything with JavaScript — except, that is, for changing location, which is what links are for. When clicked, it should toggle the type attribute between
password
and text
; and the button’s label between “Show” and “Hide.”
function PasswordReveal(input) {
// store input as a property of the instance
// so that it can be referenced in methods
// on the prototype
this.input = input;
this.createButton();
};
PasswordReveal.prototype.createButton = function() {
// create a button
this.button = $('<button type="button">Show password</button>');
// inject button
$(this.input).parent().append(this.button);
// listen to the button's click event
this.button.on('click', $.proxy(this, 'onButtonClick'));
};
PasswordReveal.prototype.onButtonClick = function(e) {
// Toggle input type and button text
if(this.input.type === 'password') {
this.input.type = 'text';
this.button.text('Hide password');
} else {
this.input.type = 'password';
this.button.text('Show password');
}
};
JavaScript Syntax and Architectural Notes
As there are many flavors of JavaScript, and different ways in which to architect components, we’re going to walk through the choices used to construct the password reveal component, and all the upcoming components in the book.
First, we’re using a constructor. A constructor is a function conventionally written in upper camel case — PasswordReveal
, not passwordReveal
. It’s initialized using the new
keyword, which lets us use the same code to create several instances of the component:
var passwordReveal1 = new PasswordReveal(document.getElementById('input1'));
var passwordReveal2 = new PasswordReveal(document.getElementById('input2'));
Second, the component’s methods are defined on the prototype — for example, PasswordReveal.prototype.onButtonClick
. The prototype is the most performant way to share methods across multiple instances of the same component.
Third, jQuery is being used to create and retrieve elements, and listen to events. While jQuery may not be necessary or preferred, using it means that this book can focus on forms and not on the complexities of cross-browser components.
If you’re a designer who codes a little bit, then jQuery’s ubiquity and low-barrier to entry should be helpful. By the same token, if you prefer not to use jQuery, you’ll have no trouble refactoring the components to suit your preference.
You may have also noticed the use of the $.proxy
function. This is jQuery’s implementation of Function.prototype.bind
. If we didn’t use this function to listen to events, then the event handler would be called in the element’s context (this
). In the example above, this.button
would be undefined. But we want this
to be the password reveal object instead, so that we can access its properties and methods.
Alternative Interface Options
The password reveal interface we constructed above toggles the button’s label between “Show password” and “Hide password.” Some screen reader users can get confused when the button’s label is changed; once a user encounters a button, they expect that button to persist. Even though the button is persistent, changing the label makes it appear not to be.
If your research shows this to be a problem, you could try two alternative approaches.
First, use a checkbox with a persistent label of “Show password.” The state will be signaled by the checked
attribute. Screen reader users will hear “Show password, checkbox, checked” (or similar). Sighted users will see the checkbox tick mark. The problem with this approach is that checkboxes are for inputting data, not controlling the interface. Some users might think their password will be revealed to the system.
Or, second, change the button’s state
— not the label. To convey the state to screen reader users, you can switch the aria-pressed
attribute between true
(pressed) and false
(unpressed).
<button type="button" aria-pressed="true">
Show password
</button>
When focusing the button, screen readers will announce, “Show password, toggle button, pressed” (or similar). For sighted users, you can style the button to look pressed or unpressed accordingly using the attribute selector like this:
[aria-pressed="true"] {
box-shadow: inset 0 0 0 0.15rem #000, inset 0.25em 0.25em 0 #fff;
}
Just be sure that the unpressed and pressed styles are obvious and differentiated, otherwise sighted users may struggle to tell the difference between them.
Microcopy
The label is set to “Choose password” rather than “Password.” The latter is somewhat confusing and could prompt the user to type a password they already possess, which could be a security issue. More subtly, it might suggest the user is already registered, causing users with cognitive impairments to think they are logging in instead.
Where “Password” is ambiguous, “Choose password” provides clarity.
Button Styles
What’s a button? We refer to many different types of components on a web page as a button. In fact, I’ve already covered two different types of button without calling them out. Let’s do that now.
Buttons that submit forms are “submit buttons” and they are coded typically as either or
. The
element is more malleable in that you can nest other elements inside it. But there’s rarely a need for that. Most submit buttons contain just text.
Note: In older versions of Internet Explorer, if you have multiple s, the form will submit the value of all the buttons to the server, regardless of which was clicked. You’ll need to know which button was clicked so you can determine the right course of action to take, which is why this element should be avoided.
Other buttons are injected into the interface to enhance the experience with JavaScript — much like we did with the password reveal component discussed earlier. That was also a but its
type
was set to button
(not submit
).
In both cases, the first thing to know about buttons is that they aren’t links. Links are typically underlined (by user agent styles) or specially positioned (in a navigation bar) so they are distinguishable among regular text. When hovering over a link, the cursor will change to a pointer. This is because, unlike buttons, links have weak perceived affordance.
In Resilient Web Design, Jeremy Keith discusses the idea of material honesty. He says: “One material should not be used as a substitute for another. Otherwise the end result is deceptive.” Making a link look like a button is materially dishonest. It tells users that links and buttons are the same when they’re not.
Links can do things buttons can’t do. Links can be opened in a new tab or bookmarked for later, for example. Therefore, buttons shouldn’t look like links, nor should they have a pointer cursor. Instead, we should make buttons look like buttons, which have naturally strong perceived affordance. Whether they have rounded corners, drop shadows, and borders is up to you, but they should look like buttons regardless.
Buttons can still give feedback on hover (and on focus) by changing the background colour, for example.
Placement
Submit buttons are typically placed at the bottom of the form: with most forms, users fill out the fields from top to bottom, and then submit. But should the button be aligned left, right or center? To answer this question, we need to think about where users will naturally look for it.
Field labels and form controls are aligned left (in left-to-right reading languages) and run from top to bottom. Users are going to look for the next field below the last one. Naturally, then, the submit button should also be positioned in that location: to the left and directly below the last field. This also helps users who zoom in, as a right-aligned button could more easily disappear off-screen.
Text
The button’s text is just as important as its styling. The text should explicitly describe the action being taken. And because it’s an action, it should be a verb. We should aim to use as few words as possible because it’s quicker to read. But we shouldn’t remove words at the cost of clarity.
The exact words can match your brand’s tone of voice, but don’t exchange clarity for quirkiness.
Simple and plain language is easy for everyone to understand. The exact words will depend on the type of service. For our registration form “Register” is fine, but depending on your service “Join” or “Sign up” might be more appropriate.
Validation
Despite our efforts to create an inclusive, simple, and friction-free registration experience, we can’t eliminate human error. People make mistakes and when they do, we should make fixing them as easy as possible.
When it comes to form validation, there are a number of important details to consider. From choosing when to give feedback, through to how to display that feedback, down to the formulation of a good error message — all of these things need to be taken into account.
HTML5 Validation
HTML5 validation has been around for a while now. By adding just a few HTML attributes, supporting browsers will mark erroneous fields when the form is submitted. Non-supporting browsers fall back to server-side validation.
Normally I would recommend using functionality that the browser provides for free because it’s often more performant, robust, and accessible. Not to mention, it becomes more familiar to users as more sites start to use the standard functionality.
While HTML5 validation support is quite good, it’s not implemented uniformly. For example, the required attribute can mark fields as invalid from the outset, which isn’t desirable. Some browsers, such as Firefox 45.7, will show an error of “Please enter an email address” even if the user entered something in the box, whereas Chrome, for example, says “Please include an ‘@’ in the email address,” which is more helpful.
We also want to give users the same interface whether errors are caught on the server or the client. For these reasons we’ll design our own solution. The first thing to do is turn off HTML5 validation: