Making Compact Forms More Accessible
Issue № 229

Making Compact Forms More Accessible

Forms pose a series of usability and accessibility challenges, many of which are made more complex when you need to build a form to fit into a small space. Tightly spaced forms can look great on paper, but they often ignore accessibility issues altogether.

Article Continues Below

A designer recently handed me a compact-form design that included the oft-seen technique of putting field names directly inside of the text fields themselves.

The typical method of marking this up is to put the field name in the value attribute of each input element. You would then throw in some JavaScript and server-side scripting to make sure that the user didn’t submit the form with the default values of “username” and “password.” Password fields, though, are designed to safely hide input from prying eyes, replacing each character with an asterisk or bullet. So this method prevents us from putting anything useful into the password field—and even if this were not the case, default values provide no truly accessible information about the form itself.

Sample layout of a compact form

Figure 1: Example of a compact-form with the field names inside text fields.

In this article, we’ll create a compact form that provides a high degree of accessibility, despite its reduced size. Note that I’m not saying that tiny forms are a good idea. Rather, I’m providing a means for reconciling the space-saving aims of your client (or boss, or designer) with your desire to offer a good user experience.

Starting with accessible markup#section2

To maintain accessibility, each form element should have an associated label that identifies that element’s purpose. Despite its compact design, there’s no reason our form can’t meet this guideline. As with any new site or page, we’ll start with plain-vanilla markup that is functionally correct and accessible and then we’ll use style sheets and JavaScript to enhance layout and functionality.

In this demonstration, we’ll use style sheets to make it look like form labels are actually inside of the fields themselves, but they will in fact be separate elements. (Line wraps marked » —Ed.)

<form name="login" acti method="post"> 

     <div id="username">     
	<label for="username-field" »
class="overlabel">Username</label>
	<input id="username-field" type="text" »
name="username" title="Username" »
value="" tabindex="1" />
	</div>

	<div id="password">
	<label for="password-field" »
class="overlabel">Password</label>
	<input id="password-field" type="password" »
name="password" title="Password" »
value="" tabindex="2" />
	</div>

	<div id="submit">
	<input type="submit" name="submit" »
value="Login" tabindex="3" />
	</div>

</form>

Each label and input element is wrapped in a div to provide a clean appearance when it’s viewed without a style sheet. You could wrap these in fieldset elements instead, but in my opinion, a single field does not make a set.

We’ve given each of the labels the class name of “overlabel.” Rather than using this class as a CSS selector, we’ll use JavaScript to locate all label elements with this class name and apply event handlers to each of them. Using classes instead of ids allows us to easily add functionality to sets of elements without having to keep track of specific ids.

In addition to the label tags, we’ve added title attributes to each input field to enhance usability for sighted viewers, since the label itself will be hidden from view once the user enters information into each field.

Next, we’ll add the styles needed to overlay each label.

form#login {
	position:relative;
}

div#username,
div#password {
	position:relative;
	float:left;
	margin-right:3px;
}

input#username-field,
input#password-field {
	width:10em;
}

label.overlabel {
	position:absolute;
	top:3px;
	left:5px;
	z-index:1;
	color:#999;
}

There’s nothing surprising about the style sheet. Using absolute positioning on the labels takes them out of the flow of the form, and since the divs are floated to the left, the input fields line up next to each other without any extra spacing. This works especially well for my form, where these fields lie next to each other in a horizontal arrangement. The label elements have also been given a z-index because they will sit in front of other field elements.

Depending on the size of your font and form fields, you may need to juggle the positioning for the labels, but this example renders consistently across all modern browsers. We’ll use scalable units—in this case, ems—to allow users to resize the text size in their browsers and ensure that the text fields and labels grow proportionally.

Add a reusable script#section3

The labels for each field need to be hidden when the associated field is selected, and if there is a value in either of the fields, its label should stay hidden. This should be the case whether the page was loaded with a value in the value attribute or if it was added by the user after the page is loaded.

We’ll be relying on JavaScript to hide the labels, so we’re going to make a small swap in the “overlabel” class we defined. The labels should remain visible if JavaScript is not available.

label.overlabel {
	color:#999;
}  
  
label.overlabel-apply {
	position:absolute;
	top:3px;
	left:5px;
	z-index:1;
	color:#999;
}

To maintain accessibility of the labels, we will use a negative text-indent to hide the text from view, rather than setting the display to “none.” (Line wraps marked » —Ed.)

function initOverLabels () {
  if (!document.getElementById) return;        var labels, id, field;  // Set focus and blur handlers to hide and show 
  // labels with 'overlabel' class names.
  labels = document.getElementsByTagName('label');
  for (var i = 0; i < labels.length; i++) {    if (labels<i>.className == 'overlabel') {      // Skip labels that do not have a named association
      // with another field.
      id = labels<i>.htmlFor || labels<i>.getAttribute »
('for');
      if (!id || !(field = document.getElementById(id))) {
        continue;
      }       // Change the applied class to hover the label 
      // over the form field.
      labels<i>.className = 'overlabel-apply';      // Hide any fields having an initial value.
      if (field.value !== '') {
        hideLabel(field.getAttribute('id'), true);
      }      // Set handlers to show and hide labels.
      field.onfocus = function () {
        hideLabel(this.getAttribute('id'), true);
      };
      field.onblur = function () {
        if (this.value === '') {
          hideLabel(this.getAttribute('id'), false);
        }
      };      // Handle clicks to label elements (for Safari).
      labels<i>.onclick = function () {
        var id, field;
        id = this.getAttribute('for');
        if (id && (field = document.getElementById(id))) {
          field.focus();
        }
      };    }
  }
};function hideLabel (field_id, hide) {
  var field_for;
  var labels = document.getElementsByTagName('label');
  for (var i = 0; i < labels.length; i++) {
    field_for = labels<i>.htmlFor || labels<i>. »
getAttribute('for');
    if (field_for == field_id) {
      labels<i>.style.textIndent = (hide) ? '-1000px' : »
'0px';
      return true;
    }
  }
}window.onload = function () {
  setTimeout(initOverLabels, 50);
};

The script looks through all of the labels on the page for any that include the class name “overlabel.” It locates associated fields based on the value of the label’s for attribute, which should match an input tag’s id. Our new class, “overlabel-apply”, is applied to each of these labels. Additionally, onfocus and onblur event handlers are added to these input fields that will control the text-indent of associated labels, hiding them from view.

As mentioned above, this script works independently from the names and ids of the input fields. It identifies labels with the class name of “overlabel.” Just as class can be used to apply styles to sets of elements on a page, so we use it here to add functionality to those same elements. (Hat tip to Daniel Nolan, whose Image Rollover code demonstrated this approach to me a few years ago.)

It may strike you as odd that the onload handler uses a short timeout, but this pause allows us to accommodate browsers that offer to save login credentials for frequently-visited sites. These browsers often insert saved values into HTML forms after the page has completed loading, and a built-in pause seems to ensure that a label won’t be left hovering on top of your saved account information if this happens.

Note that only in the last section of the initOverLabels function do we have to provide any browser-specific code. Clicking on a label element typically passes the cursor’s focus to the related input field, but not so with Safari, where the label effectively blocks the user from selecting the field. To get around this, we add an onclick handler to the label that simply passes the focus for us.

Summary#section4

This working example provides a clean, accessible technique for visually locating a field name inside of the field itself without tampering with the initial value. Submitting the form without touching any of the fields provides an empty set of data, rather than sending “username” and “password” to your script as values. Furthermore, we can easily add these “overlabels” to any form by adding a class name, a little bit of CSS, and the JavaScript code provided above.

As icing on the cake, if the user visits the site without JavaScript or CSS capabilities, the form is still usable and the labels will lie next to each field as was originally intended before your designers got involved.

About the Author

Mike Brittain

Mike Brittain is a senior web developer and architect at Heavy.com. He lives in New York City, where he spends far too much time and money planning for his next big ski trip.

56 Reader Comments

  1. (I also posted this exact comment on the author’s personal blog where he mentioned the publishing of his article on this site. But I thought I would post here in case anyone would be interested in lending a hand to me)

    Great tutorial. Newbie to web design here (CSS, JavaScript, etc.)

    Background:
    I wanted to make a search-box for the main page of my site that is sleek-looking with the label within the search field. When I did a simple google search, I saw something about using ‘onfocus’. This simple solution worked fine in Firefox, but when I tried that in IE6, a warning came on and I had to select “allow” to get the proper effect. I didn’t want to force my users to have to do that.

    So I searched again. I saw in another forum that someone said to do a google search for “compact form” and that brought me to this article.

    Using the techniques published, now IE6 fires up the form just fine without having the user select to “allow” the interactive functionality.

    I was wondering about how to go about two things:

    1) I would like the value that’s in the field to say “Please enter anything (name, city, zip, etc.) to get started” (I will make the field long enough to accomodate this). But I don’t think I want this sentence to be a label should JS be disabled.

    Right? Or maybe it would be ok to let the label be this sentence, but wouldn’t it be too long with spaces and everything to be used correctly with the code?

    Any advice on this?

    Also, 2) What if I wanted two different submit boxes – one for “find” and one for “add” depending on what they wanted to do with what they entered. What’s the best way to go about this?

    Any ideas would be much appreciated.

  2. form#login {
      position:relative;
    }
    
    div#username,
    div#password {
      position:relative;
      float:left;
      margin-right:3px;
      z-index:1;
    }
    
    input#username-field,
    input#password-field {
      width:10em;
      background:transparent;
    }
    
    input:focus {
      background:white !important;
    }
    
    label.overlabel {
      position:absolute;
      top:3px;
      left:5px;
      z-index:-1;
      color:#999;
    }
    
  3. I’m encountering the problem that when the password field is automatically set based off of the user name value the label is not being removed.

    Has anyone else encountered this?

    It ends up with the starred password overlaying on the label that says “password”. I’ve tried to modify the script in various ways to correct for this, but have had no luck.

    Can anyone help?

  4. Accessible means “no loss of information for everyone”.

    Here, when a text field has the focus or when it has a default value, how can the user know what he is supposed to enter? The label does not appear: information is missing. Not accessible. However, this is accessible for blind people, with a screen reader that doesn’t care about where is the label, visually speaking.

    Please remember that accessibility is not only clean and well-formed XHTML code. And remember that blindness is not the only disability in the world.

  5. Have to say this is a great idea.. Top Job! Only thing I would have said but has already been mentioned is the use of a fieldset and list to lay it out.

  6. Westbrook, having the same issue with auto filled password field here. I haven’t found a reliable solution to fix this, unfortunately.

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA

I am a creative.

A List Apart founder and web design OG Zeldman ponders the moments of inspiration, the hours of plodding, and the ultimate mystery at the heart of a creative career.
Career