While they may not be that pretty, forms are one of the most important parts of a web site. I have already written an article on CSS Forms, that was over a year ago, and I have developed much better techniques.

There are other great resources for information about form design, and I will take this information into account when I am showing the flexibility of my method of marking up and styling forms.

The Markup

After previously using a definition list to mark up my forms, I have switched to using an ordered list now. I switched for two main reasons:

  • The list item of the ordered list provides a containing clearing element that the definition list does not provide.
  • Semantically, it just makes more sense. A form is really just a list of form fields to fill out. Forms are typically meant to be filled out in order, hence the ordered list instead of un-ordered list (I think this could be debated, but in the long run, either one is more semantic than anything else).

I have decided to create just a simple un-styled form to use as an example. Here is what the markup of the page is:

<ol class="forms">
 <li><label for="name">Name</label><input type="text"name="name" id="name" /></li>
 <li><label for="email">Email</label><input type="text" name="email" id="email" /></li>
 <li><label for="city">City</label><input type="text" name="city" id="city" /></li>
 <li><label for="state">State</label><input type="text" name="state" id="state" /></li>
 <li><label for="zip">Zip</label><input type="text" name="zip" id="zip" /></li>
 <li class="grouping"><label>Sign Up for the Following</label>
  <ul>
   <li><input type="checkbox" name="info1" id="info1" /><label for="info1">Information 1</label></li>
   <li><input type="checkbox" name="info2" id="info2" /><label for="info2">Information 2</label></li>
   <li><input type="checkbox" name="info3" id="info3" /><label for="info3">Information 3</label></li>
  </ul>
 </li>
 <li><label for="message">Message</label><textarea name="message" id="message"></textarea></li>
 <li class="buttons"><button type="submit" name="submit" id="submit">Submit</button></li>
</ol> 

Even without any style applied, the form is still completely usable.

Styling the Forms

There are really 3 main styles of laying out forms:

  • Labels above the form field
  • Labels to the side of the form field, jagged right
  • Labels to the side of the form field, jagged left

Again, like I said, I am not going to go through the pros and cons of each method, I am going to show how easy it is to switch between the methods using my markup.

Labels Above the Form Field

This is by far the easiest method. Here is the CSS used to style my example form:

ol.forms { float: left; list-style: none; width: 100%; }
ol.forms li { 
 clear: both; 
 float: left; 
 margin: 0 0 10px; 
 width: 100%; 
}
ol.forms label { cursor: pointer; display: block; font-weight: bold; }
ol.forms input, ol.forms textarea { 
 font: inherit;
 padding: 2px;
 width: 300px;
}
ol.forms textarea { height: 250px; }
ol.forms li.grouping { margin-bottom: 0; }
ol.forms li.grouping ul { list-style: none; }
ol.forms li.grouping ul label { 
 display: inline;
 font-weight: normal;
 margin: 0 0 0 10px;
}
ol.forms li.grouping ul input { width: auto; } 

Really nothing too complicated. In my opinion, I don’t like stacking the label over the form field; I think it just makes the form too long.

Labels to the Side of the Form Field, Jagged Right

I like this layout of forms the best. I think it presents the form in a clear way so that you can just scan right through the form fields. With just a few changes to the CSS, you can float the labels to the left of the form fields. The changes are marked in bold:

ol.forms { float: left; list-style: none; width: 100%; }
ol.forms li { 
 clear: both;
 float: left;
 margin: 0 0 10px;
 width: 100%;
}
ol.forms label { 
 cursor: pointer;
 display: block;
 <strong>float: left;</strong>
 font-weight: bold;
 <strong>margin: 0 10px 0 0;
 width: 90px;</strong>
}
ol.forms input, ol.forms textarea { 
 font: inherit;
 padding: 2px;
 width: 300px;
}
ol.forms textarea { height: 250px; }
<strong>ol.forms li.grouping label { margin: 0; width: auto; }</strong>
ol.forms li.grouping { margin-bottom: 0; }
ol.forms li.grouping ul { list-style: none; <strong>margin-left: 100px;</strong> }
ol.forms li.grouping ul label { 
 display: inline;
 <strong>float: none;</strong>
 font-weight: normal;
 margin: 0 0 0 10px;
 <strong>width: auto;</strong>
}
ol.forms li.grouping ul input { width: auto; }
<strong>ol.forms li.buttons { float: none; margin-left: 100px; width: auto; }</strong> 

Take a look at the example with the fields on the left. Depending upon your label length, you may need to increase the width of the labels so that it does not span more than one line, but that is an easy enough change.

Labels to the Side of the Form Field, Jagged Left

Now with two changes to the CSS, you can easily switch to having the labels jagged left, which puts them closer to the form input. The changes needed are highlighted in bold:

ol.forms label { 
 cursor: pointer;
 display: block;
 float: left;
 font-weight: bold;
 margin: 0 10px 0 0;
 <strong>text-align: right;</strong>
 width: 90px;
}
ol.forms li.grouping label { margin: 0; <strong>text-align: left;</strong> width: auto; } 

Here is the example of the form with the labels jagged left.

Taking it a Step Further with More CSS and a Little jQuery

Since I like the form layout with labels to the side and jagged right, I am going to use this one to customize the display a little, and then add in some jQuery functionality.

Improving the Display

When looking at the form, it may be confusing to have all form fields be the same width. It is helpful to the user to modify the width to suit the data that will be entered.

For example, there is no reason that the zip code, city, and state fields should be so long, so let’s add a class to each one to shorten it to the length of the data that will probably be entered. Here is the modified markup:

<li>
 <label for="city">City</label>
 <input type="text" name="city" id="city" class="medium" />
</li>
<li>
 <label for="state">State</label>
 <input type="text" name="state" id="state" class="small" />
</li>
<li>
 <label for="zip">Zip</label>
 <input type="text" name="zip" id="zip" class="extraSmall" />
</li> 

Then we just have to add those 3 classes to the CSS:

ol.forms .extraSmall { width: 50px; }
ol.forms .small { width: 100px; }
ol.forms .medium { width: 150px; } 

Here is what the form looks like with that little bit of extra style. I also added in some help text, a notation for required fields, and the associated CSS; so take a look at the code to see that.

Adding a Little jQuery

Obviously you have read my article about AJAX forms with jQuery, which discusses how to add AJAX functionality to a form in a degradable way. So the following code assumes that you are going to be doing server side validation of the form as well.

Since there are required fields in this form, I add a class of required to the form and to each parent list item of required input fields.

First, we want our JavaScript to execute when the form with a class of required is submitted:

$(document).ready(function() {
 $('form.required').submit(function() {
  &hellip;
 });
}); 

If there are any error messages still displaying from before, we want to hide them. Also, let’s disable the submit button so just in case, the form cannot be submitted twice. I am also creating a variable that I will be using later to determine if there is an error or not:

$(document).ready(function() {
 $('form.required').submit(function() {
  <strong>$('form.required span.error').remove();
  $('form.required li.buttons button').attr('disabled','disabled');
  var hasError = false;</strong>
 });
}); 

Now, we want to check each form field that has a parent list item with a class of required:

$(document).ready(function() {
 $('form.required').submit(function() {
  $('form.required span.error').remove();
  $('form.required li.buttons button').attr('disabled','disabled');
  var hasError = false;

  <strong>jQuery.each($('form.required ol.forms li.required'),function() {
   &hellip;
  });</strong>
 });
}); 

There are a lot of other jQuery plugins that validate forms, but most of the time, it will just alert the user that the field is required. Instead of doing that, I am going to grab the text from the label element and display that in the error message:

$(document).ready(function() {
 $('form.required').submit(function() {
  $('form.required span.error').remove();
  $('form.required li.buttons button').attr('disabled','disabled');
  var hasError = false;

  jQuery.each($('form.required ol.forms li.required'),function() {
   <strong>var labelText = $(this).children('label').text();
   labelText = labelText.replace(' *','');</strong>
  });
 });
}); 

Now, we have two different structures of form fields for each list item: a single form field and a grouping of checkboxes or radio buttons. We have to do things a little bit different for each one:

$(document).ready(function() {
 $('form.required').submit(function() {
  $('form.required span.error').remove();
  $('form.required li.buttons button').attr('disabled','disabled');
  var hasError = false;

  jQuery.each($('form.required ol.forms li.required'),function() {
   var labelText = $(this).children('label').text();
   labelText = labelText.replace(' *','');

   <strong>if($(this).hasClass('grouping')) {
    &hellip; 
   } else {
    &hellip;
   }</strong>
  });
 });
}); 

First, let’s focus on the case when there is a grouping. We want to loop through each input and see if it is checked. If it is, we just increment a counter. Then, if the counter equals zero, meaning we have no fields checked, we display an error message:

$(document).ready(function() {
 $('form.required').submit(function() {
  $('form.required span.error').remove();
  $('form.required li.buttons button').attr('disabled','disabled');
  var hasError = false;

  jQuery.each($('form.required ol.forms li.required'),function() {
   var labelText = $(this).children('label').text();
   labelText = labelText.replace(' *','');

   if($(this).hasClass('grouping')) {
    <strong>var numSelected = 0;
    jQuery.each($(this).find('input'),function() {
     if($(this).attr('checked') == true) {
      numSelected++;
     }
    });
    
    if(numSelected == 0) {
     $(this).append('<span class="error">You must select from '+labelText+'.</span>');
     hasError = true;
    }</strong>
   } else {
    &hellip;
   }
  });
 });
}); 

That seems easy enough, and it’s even easier for the single input fields. We just check to see if the value is empty, and if it is, display the error message:

$(document).ready(function() {
 $('form.required').submit(function() {
  $('form.required span.error').remove();
  $('form.required li.buttons button').attr('disabled','disabled');
  var hasError = false;

  jQuery.each($('form.required ol.forms li.required'),function() {
   var labelText = $(this).children('label').text();
   labelText = labelText.replace(' *','');

   if($(this).hasClass('grouping')) {
    var numSelected = 0;
    jQuery.each($(this).find('input'),function() {
     if($(this).attr('checked') == true) {
      numSelected++;
     }
    });
    
    if(numSelected == 0) {
     $(this).append('<span class="error">You must select from '+labelText+'.</span>');
     hasError = true;
    }
   } else {
    <strong>if(jQuery.trim($(this).children('input, textarea').val()) == '') {
     $(this).append('<span class="error">Please enter your '+labelText+'.</span>');
     hasError = true;
    }</strong>
   }
  });
 });
}); 

To finish up, we just check to see if the hasError variable is true. Since this is just a demo form, I just add an alert if the form would submit successfully:

$(document).ready(function() {
 $('form.required').submit(function() {
  $('form.required span.error').remove();
  $('form.required li.buttons button').attr('disabled','disabled');
  var hasError = false;

  jQuery.each($('form.required ol.forms li.required'),function() {
   var labelText = $(this).children('label').text();
   labelText = labelText.replace(' *','');

   if($(this).hasClass('grouping')) {
    var numSelected = 0;
    jQuery.each($(this).find('input'),function() {
     if($(this).attr('checked') == true) {
      numSelected++;
     }
    });
    
    if(numSelected == 0) {
     $(this).append('<span class="error">You must select from '+labelText+'.</span>');
     hasError = true;
    }
   } else {
    if(jQuery.trim($(this).children('input, textarea').val()) == '') {
     $(this).append('<span class="error">Please enter your '+labelText+'.</span>');
     hasError = true;
    }
   }
  });

  <strong>if(hasError) {
   $('form.required li.buttons button').removeAttr('disabled');
   return false;
  } else {
   alert('The form would submit now');
   $('form.required li.buttons button').removeAttr('disabled'); //Remove this line if using for real
   return false; //Change this to true when using it for real
  }</strong>
 });
}); 

Go ahead and take a look at the demo form to see it in action.

Conclusion

With a well structured form, you can easily customize it to your liking with just a little bit of CSS. Then, by using some custom jQuery code, you can easily customize how you would like to have the form validation appear.

With a little more work, you could easily add in validation for things like form elements expecting an email address, zip codes, phone numbers, etc. I will have to save that for another post since this one is quite long.

What do you all think? How do you structure your forms? Which layout of forms do you prefer and why?