Should I use tables to lay out my form?
A question I get asked a lot concerns the relative merits of using tables to layout forms. Certainly, this was the norm for many years. However, as I will point out below, tables are no more appropriate as a layout tool for forms than they are for the page as a whole.
The best place to start, I think, is to address the main concern people have about not using tables for forms. It has to do with control. As I have pointed out in other posts, and I know I will need to point out again, the web is not a medium for control freaks. One of the main concepts behind the web, although unfortunately far too few books and courses on web design mention this, is that the user is supposed to be in control, not the designer.
How this relates to the tables-and-forms issue is that without tables, I will freely admit that it is difficult (although not impossible) to get form fields to neatly line up with one another. I don't want to sound too rude, but my answer to that is ultimately, "so what?" Believe it or not, it is entirely possible to create a form where the fields do not perfectly line up, and yet still have that form look very nice.
So now let's get to the specifics of why tables shouldn't be used for forms. There are several reasons that come to me off the top of my head:
- Forms are not tabular data.
- Tables needlessly bloat form code
- Table-based forms are generally less accessible
- You cannot effectively use fieldsets and legends.
Let's take a look at each of these reasons in detail.
Reason 1: Forms are not tabular data
The biggest focus of the web standards movement is to get web designers to write semantic code. That is, use the HTML or XHTML elements that make the most sense in the current context. In the case of tables, that means limiting their use to tabular data, which disqualifies forms. A form may have a set of labels and a set of form fields, and the designer might desire that these be laid out in columns, but that does not make it tabular and it certainly does not make it data.
Reason 2: Tables needlessly bloat code
If you wish to create a form without tables, you will still need to wrap each field and its label in something. Many designers (myself included) use paragraphs. While the argument can be made that this is not strictly semantic, no other block-level element is, either. The argument can be made to wrap them in a div, but the end result would be the same, so I think that which one you choose to use will be personal preference.
Example of a Table-based Form
Below is an example of a simple form that relies on tables.
Example of a Form without a Table
And now the same form, using paragraphs instead.
Code Comparison
Let's examine the code under each one to first get an idea of how bloated the table really is.
Form with a Table
<table width="400" border="0">
<tr>
<td><label for="firstname">First Name:</label></td>
<td><input type="text" name="firstnam" id="firstname" /></td>
</tr>
<tr>
<td>Last Name:</td>
<td><input type="text" name="lastname" id="lastname" /></td>
</tr>
<tr>
<td>Address:</td>
<td><input type="text" name="address" id="address" /></td>
</tr>
<tr>
<td>City:</td>
<td><input type="text" name="city" id="city" /></td>
</tr>
<tr>
<td>State:</td>
<td>
<select name="state" id="state">
<option value="al">Alabama</option>
<option value="ak">Alaska</option>
<option value="ar">Arkansas</option>
</select> </td>
</tr>
<tr>
<td>Zip:</td>
<td><input type="text" name="zip" id="zip" /></td>
</tr>
<tr>
<td>Age Range:</td>
<td><select name="agerange" id="agerange">
<option value="under20">Under 20</option>
<option value="21-30">21-30</option>
<option value="31-40">31-40</option>
<option value="41-50">41-50</option>
<option value="over50">Over 50</option>
</select></td>
</tr>
<tr>
<td>Gender:</td>
<td>
<label for="MGender">
<input type="radio" name="gender" value="M" id="MGender" />
Male</label>
<br />
<label for="FGender">
<input type="radio" name="gender" value="F" id="FGender" />
Female</label> </td>
</tr>
<tr>
<td>Comments:</td>
<td><textarea name="comments" id="comments" cols="45" rows="5"></textarea></td>
</tr>
</table>
</form>
The Same Form without the Table
<p><label for="firstname">First Name:
<input type="text" name="firstnam" id="firstname" /></label></p>
<p><label for="lastname">Last Name:
<input type="text" name="lastname" id="lastname" /></label></p>
<p><label for="address">Address:
<input type="text" name="address" id="address" /></label></p>
<p><label for="city">City:
<input type="text" name="city" id="city" /></label></p>
<p><label for="state">State:
<select name="state" id="state">
<option value="al">Alabama</option>
<option value="ak">Alaska</option>
<option value="ar">Arkansas</option>
</select></label></p>
<p><label for="zip">Zip:
<input type="text" name="zip" id="zip" /></label></p>
<p><label for="agerange">Age Range:
<select name="agerange" id="agerange">
<option value="under20">Under 20</option>
<option value="21-30">21-30</option>
<option value="31-40">31-40</option>
<option value="41-50">41-50</option>
<option value="over50">Over 50</option>
</select></label></p>
<p>Gender:
<label for="MGender">
<input type="radio" name="gender" value="M" id="MGender" />
Male</label>
<br />
<label for="FGender">
<input type="radio" name="gender" value="F" id="FGender" />
Female</label>
</p>
<p><label for="comments">Comments:
<textarea name="comments" id="comments" cols="45" rows="5"></textarea></label>
</p>
</form>
The code that uses the table requires 15% more code than does the code that does not. Since the form code is identical, every bit of the additional code is the table. That means that the first form will take 15% more time to load, and more important, it will use 15% more bandwidth each time a user visits the site to fill in the fom.
Reason 3: Table-based forms are generally less accessible
Screen readers for the blind will encounter a table and assume that it contains data. The initial presentation of the table to the blind user might therefore be confusing, as they may be told that what follows is a table ... oh, no, wait ... it's a form. In addition, it can become difficult for the screen reader to correctly associate the labels with the fields. Using the <label> tag will help here, but forms without tables will nonetheless be more accessible.
The problem with labels in tables for Dreamweaver users
If you're a Dreamweaver user, there is another issue to consider. The usability and accessibility of forms can be dramatically enhanced by using the <label> tag. If you use tables for layout, the label text, with its associated tag, will need to be associated with the form field by adding the for attribute:
<td><input type="text" name="firstname" id="firstname" /></td>
Unfortunately, Dreamweaver can make this difficult. There are two ways to get the <label> tag into your document. First, you can click the Label button on the Insert bar. However, this takes you into the Split view, forcing you to manually add the for attribute and its value. Second, you can insert the form field itself, which will bring up the Accessibility Attributes dialog. From here, you can specify the text to be inserted and select the option to 'Attach the label with the "for" attribute'. However, if you are using tables, you will then need to move the label from the cell with the form field to its own cell, at which point Dreamweaver will reset the for attribute's value. While it can be argued that it doesn't make a lot of sense that this happens, it does. None of this is an issue, though, if you're not using tables.
Reason 4: You cannot effectively use fieldsets and legends
I like fieldsets - personally, I don't think there's anything you can add to a form that will make it look better and make it easier to use than fieldsets. Fieldsets not only improve the appearance of the form, they also improve its accessibility and usability. The problem is that portions of tables cannot be nested within a fieldset. You can only wrap a fieldset around a complete table. Some might argue here that you can use use both - have each set of related fields be in a fieldset, and then use a table within the fieldset. That's fine ... except that it becomes difficult to get the fields within one set to line up with the fields in another since they will be in different tables, so then if the fields won't be lined up, why bother with the table at all?
But wait. Remember how I said that using fieldsets can dramatically improve the appearance of a form? By applying CSS to the fieldsets and legends, we can create a form that looks a lot better than its table-based equivalent. Let's look at the same form from above, with the addition of fieldsets and legends. We'll get to some basic CSS in a minute.
The addition of the fieldsets allows us to logically group related fields. It is easier for all users to work in the form, if for no other reason than that small sets of grouped fields are less intimidating than one big form. Note that we are also able to use a nested fieldset to solve the problem of how to deal with the label for the radio button group.
At first, it does not look so great with the fieldsets stretching across the screen, but that can be solved with a little CSS. I'm also going to make the top-level legends stand out a bit more while I'm at it.
fieldset {
width: 400px;
}
fieldset fieldset {
width: 100px;
}
legend {
font-weight:bold;
font-size: 110%;
}
fieldset fieldset legend {
font-weight: normal;
font-size: 100%;
}
</style>
The result:
Lining Up Fields
The argument most often made about forms without tables is that the fields do not line up nicely. As I mentioned in the introduction, I personally do not see this as being such a big deal. However, there are several methods around the problem.
Use Line Breaks
One solution is to place a break tag (<br />) after each label, thereby placing the field on a separate line from the label:
This solves the lined-up fields problem quite nicely, and is in fact the method I prefer to use.
Use CSS floats
Another approach that still allows you to create the grid-like feel of tables is to use CSS to float the fields next to the labels. This will require two slight changes to the XHTML. First, you need to not wrap the fields in the label tag, but instead use the for attribute. The second problem is the radio group. One reason why I don't care for this approach is that I like using the nested fieldset for the radio group, but there's no good way to get it to fit in with this style. So we'll go with a slightly less semantic approach whereby we dump the nested fieldset, place the entire group in a div, and then wrap the group's label (formerly the legend of the fieldset) in a span. Then, we need to wrap the set of input tags in its own span, for reasons I'll explain in a minute.
<fieldset> <legend>Your Name</legend>
<p>
<label for="firstname3">First Name:</label>
<input type="text" name="firstname2" id="firstname3" />
</p>
<p>
<label for="lastname3">Last Name:</label>
<input type="text" name="lastname3" id="lastname3" />
</p>
</fieldset>
<fieldset>
<legend>Your Address</legend>
<p>
<label for="address3">Address:</label>
<input type="text" name="address3" id="address3" />
</p>
<p>
<label for="city3">City:</label>
<input type="text" name="city3" id="city3" />
</p>
<p>
<label for="state3">State:</label>
<select name="state3" id="state3">
<option value="al">Alabama</option>
<option value="ak">Alaska</option>
<option value="ar">Arkansas</option>
</select>
</p>
<p>
<label for="zip3">Zip:</label>
<input type="text" name="zip3" id="zip3" />
</p>
</fieldset>
<fieldset>
<legend>Other Information</legend>
<p>
<label for="agerange3">Age Range:</label>
<select name="agerange3" id="agerange3">
<option value="under20">Under 20</option>
<option value="21-30">21-30</option>
<option value="31-40">31-40</option>
<option value="41-50">41-50</option>
<option value="over50">Over 50</option>
</select>
</p>
<div class="radiogroup">
<span class="radiolegend">Gender:</span>
<span class="radiobuttons">
<input type="radio" name="gender" value="M" id="MGender3" />
<label for="MGender3">Male</label>
<br />
<input type="radio" name="gender" value="F" id="FGender3" />
<label for="FGender3">Female</label>
</span>
</div>
<label for="comments3">Comments:</label>
<textarea name="comments3" id="comments3" cols="45" rows="5"></textarea>
</p>
</fieldset>
</form>
Next, you will need to apply the CSS. First, you will need to make the label, input, select, and textarea elements display as blocks. We will also want to add a bottom margin so that the fields have a little space.
display: block;
margin-bottom: 10px;
}
You can use opposing floats to get the fields next to the labels. I'm going to give the labels a width of 20%, and the fields 70%. My percentages do not add up to 100 so that any default left and right padding and margins will not become a problem.
float: left;
width: 20%;
}
input, select, textarea {
float: right;
width: 70%;
}
Again, the radio group is a bit of a problem. Instead of the labels floating opposite the field, we need to float the span that contains the group's label opposite the span that contains the fields.
display: inline;
}
.radiogroup span {
display: block;
}
.radiogroup .radiolegend {
float: left;
width: 20%;
}
.radiogroup .radiobuttons {
float: right;
width: 70%;
}
You may need to toss in an empty div with a class that clears the float to prevent elements that follow the group from floating into its space. Your XHTML would be:
And the CSS:
clear: both;
}
Whew. That took some doing, but here's the result:
Conclusion
So there you have it. Forms that are laid out with tables do not look dramatically better, and in fact, forms that do not use them provide more flexibility in design, a topic that will be explored in even more detail in a later post. They use more code. They are harder to make accessibile and usable, and they are harder to create in Dreamweaver.

Thanks for showing me another method to layout forms. I had not seen this before. Your session today at TODCon 2008 was great!
Now to make a blog entry of my own, that links back here, to make my life easier. :)
-nolan
While I'd love to adopt CSS-only forms, I'm still in a position of supporting users with a
corporate IE6 browser, and CSS-only has some pretty serious vertical alignment problems.
In fact, if you view this very page in IE6, you'll note that in the final example the "State"
label seems to straddle the City and State fields. Firefox v2 does the same thing, so it may not
be just an IE6 problem. It's particularly severe with radio button groups (inline) combined
with inline small images (like a help link). Tables are the only way to get some sort of
vertical sanity and even then I need to apply local CSS to elements to get better vertical
alignment so things don't look goofy.
Unfortunately, I *am* a control freak, but my users also have tons of Windows apps that look
right directly out of the box, and I'm in competition with some of them. They don't usually
understand "semantic" and "almost". :-)
Excellent article though...one of these days maybe I'll freelance. LOL
Tim
Stephen Lane