Flexible and Efficient HTML/CSS Form Layout

Filed under

HTML 4's fieldset, legend, and label tags took longer to find their way into my toolbox than I'd care to admit. In fact, it took Dreamweaver's use of them for form design a few years back to bring them to my attention. Since then I've combined several useful tips from experts to create my own standard form layout style sheet. I'll share my thoughts behind my form style sheet, basic usage examples, and the HTML and CSS source for you to download and try.

Forms fall into two layout types, expanded and compact. Each type has its strengths, weaknesses, and optimal use cases.

Expanded Compact
  • Improved readability
  • Optimized for smaller datasets
  • Easier for new users
  • Optimal use of space, less scrolling
  • Optimized for larger datasets
  • Geared towards power-users

This form layout technique strives to use semantically and structurally correct HTML markup to provide a fluid layout. I chose not to employ JavaScript at all, although I think DOM scripting is pretty darn useful. I also tried to minimize the number of CSS classes and <div> elements. Overall, these form styles work pretty darn well in all modern browsers, including IE 6. Does everything line up perfectly? Hell no. Are there areas for improvement? Absolutely. But overall, the CSS and HTML validates and provides an solid foundation to build upon.

Basic Form Styles

The style sheet is broken into 3 main sections, base form element styles, expanded form styles, and compact form styles. The base styles are used across the board and zero out margin and padding for all form elements. I then add what I think are more reasonable margin, padding, and border values. Base classes exist to differentiate text, button, and readonly inputs until there's better browser support for CSS attribute selectors. I've also added generic width classes that can be used on just about any element.

/* Zero out padding and margin, adjust where needed later */ 
form, fieldset, label, input, .checkbox, textarea, select, option, form div {
  margin: 0;
  padding: 0;
}
fieldset {
  margin: 1em 0;
  padding: 1em;
  border: 1px solid #999;
}
legend {
  font-weight: bold;
  margin: 0 2% .5em 0;
  padding: .2em .5em;
}
input.text, textarea, select {
  border: 1px solid #666;
  border-right: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
}
option {
  float: none;
  clear: both;
  margin-right: 2em;
}
input.radio, input.checkbox {
  margin-right: .5em;
}
/* Create a bit of padding for text inputs and selects */ 
input.text, input.button, textarea, select {
  padding: .2em;
}
input.button {
  margin-right: 1em;
  padding: .2em .6em;
}
.readonly {
  color: #999;
  background: #e7e7e7;
}
/* standard widths */
.w12   { width: 12% }
.w25   { width: 25% }
.w37   { width: 37% }
.w50   { width: 50% }
.w62   { width: 62% }
.w75   { width: 75% }
.w87   { width: 87% }
.wauto { width: auto; }

With the base form element style defined the sheet then defines layout for expanded and compact form types. I make extensive use of descendant selectors to minimize the need to create additional classes.

Expanded Form Layout

The expanded form layout stacks form labels on top of their form inputs and rows upon rows. Take a look at a simple expanded form example.

Two classes are added for expanded layouts, expandedform and row. Here's the expandedform CSS with a few comments that explain some of what's going on.


.expandedform {
  overflow: hidden;
}
.expandedform .row {
  padding: .3em 0;
  white-space: nowrap;
  overflow: hidden;
  clear: both;
}
.expandedform label, .expandedform .row p, .expandedform .row div, 
.expandedform input, .expandedform select, .expandedform textarea {
  float: left;
}
/* change column widths for labels and inputs */ 
.expandedform .text {
  width: 80%;
}
.expandedform label, .expandedform .row p {
  width: 15%;
  margin: .5em 1em .5em 0;
  text-align: right;
  white-space: normal;
  overflow: hidden;
}
.expandedform .row div {
  white-space: nowrap;
  overflow: hidden;
  clear: none;
}
/* labels and form inputs inside of divs should not float */ 
.expandedform .row div * {
  float: none;
  clear: none;
}
.expanded input.checkbox, .expanded input.radio, .expanded select {
  margin-top: 1.5em;
}
.expandedform div div input {
  margin-right: 0;
}

The expandedform class is applied to the parent form element.

<form id="userinfo" class="expandedform" action="https://www.2tbsp.com/process.php" method="post">
... 
</form>

Expanded forms use the row class applied to div tags to surround each label/form element pair. Text and button classes are applied to inputs accordingly.

<div class="row">
  <label for="firstname">First name:</label>
  <input id="firstname" class="text" name="firstname" type="text">
</div>

Compact Form Layout

The compact form layout allows for multiple inputs per row. Take a peak at an example compact form.

A few more classes were required for compact forms, each targets the specific number of items in each row. The row class names indicate the number of items within the row (oneper, twoper, threeper, ...). Dealing with width, margin, and padding values was a bit tricky, especially in IE, big surprise, right? The nesting of divs and paragraphs also presented a few challenges, but the following CSS is robust for most compact form layout needs.

.compactform {
  overflow: hidden;
}
.compactform div, .compactform label, .compactform input, 
.compactform select, .compactform textarea {
  float: left;
}
.compactform input.text, .compactform select, .compactform textarea {
  /* set width: auto to allow form element content to set width otherwise 90% is a good setting */ 
  width: 90%; /* determines space between form elements */
}
/* Increase width of elements in twoper columns to provide better alignment */ 
.compactform .twoper input.text, .compactform 
.twoper select, .compactform .twoper textarea {
  width: 95%;
}
.compactform div {
  width: 99%;
  margin-bottom: 1em; /* space between rows */ 
  clear: both;
}
/* Nested divs shouldn't clear floated elements */ 
/* keeps nested divs from compounding margin value */ 
.compactform div div {
  margin-bottom: 0;
  clear: none;
}
/* Nested div label contents should determine their own width */ 
.compactform div div label {
  width: auto;
  white-space: normal; 
  /* unccomment to stack form inputs inside a row */ 
  /* clear: left; */ 
  margin-right: 1em;
}
/* Fix paragraph white space in Safari */ 
.compactform div div p {
  margin: 0;
  padding: 0;
}
/* Compact layout - Set item width within rows */ 
.oneper label, .oneper div {
  width: 99%; /* can't be 100%, IE bug */ 
}
.twoper label, .twoper div     { width: 46%; }
.threeper label, .threeper div { width: 32%; }
.fourper label, .fourper div   { width: 23%; }
.fiveper label, .fiveper div   { width: 19%; }
.sixper label, .sixper div     { width: 15%; }
.sevenper label, .sevenper div { width: 13%; }
.eightper label, .eightper div { width: 11%; }
.nineper label, .nineper div   { width: 10%; }
.tenper label, .tenper div     { width: 8.7%; }
.oneper label, .oneper div, 
.twoper label, .twoper div, 
.threeper label, .threeper div, 
.fourper label, .fourper div, 
.fiveper label, .fiveper div, 
.sixper label, .sixper div, 
.sevenper label, .sevenper div, 
.eightper label, .eightper div, 
.nineper label, .nineper div, 
.tenper label, .tenper div {
  white-space: nowrap;
  overflow: hidden;
  border: none;
  border-collapse: collapse;
}

Just like the expandedform, the compactform class is applied to the parent form element.

<form id="userinfo" class="compact" action="https://www.2tbsp.com/process.php" method="post">
... 
</form>

The appropriate row classes (oneper through tenper) are applied to div tags surrounding the corresponding number of label/form element pairs. An important difference in the use of label tags in compact forms is that they must surround the form input and the label. Here's an example of a compactform row containing three inputs.

<div class="threeper">
  <label for="firstname">
    First:
    <input id="firstname" class="text" name="firstname" type="text">
  </label>
  <label for="middlename">
    Middle:
    <input id="middlename" class="text" name="middlename" type="text">
  </label>
  <label for="lastname">
    Last:
    <input id="lastname" class="text" name="lastname" type="text">
  </label>
</div>

Conclusion

The complete CSS file containing both expanded and compact styles is below along with the form examples. These form styles have worked well for me over the past few years. Download them, change them, and come back to share your improvements!

Related Links

Share