Form layout using negative margins

Doing form layout using CSS and semantic markup is still an area of pain. Sometimes it’s nice to display a form as a table with the label on the left and the field on the right, but does this mean that it means tables are the best lightweight, semantic way to mark up forms?

I was working on a project where they were using definition lists to mark up form elements so they could be displayed with the label on the left and the form elements on the right. They were complicated forms with validation messages, multiple fields (like lists of checkboxes) and extra bits of helpful information. I didn’t really think that definition lists has any more semantic meaning than tables. It also was generating almost as much markup as just using a table.

I thought there had to be a better way so I had a bit of a think about it and came up with a way of displaying these complicated forms using only nice labels, paragraph tags and a little bit of negative margin magic.

How it works

This is pretty much exactly the same as using negative margins to position a menu bar in a two column layout. The only difference is that the element being positioned has it’s display style set to inline-block rather than block so it stays in the flow of the document and will sit happily next to the rest of the inline elements.

1. Start off with some nice simple markup. Just group all the elements you want to display for a single field in a container tag:

  1. <form id="form1" runat="server">
  2.     <p>
  3.         <label for="name">Name:</label>
  4.         <input id="name" name="name" type="text"/>
  5.     </p>
  6.     …
  7. </form>

2. Then set a margin on the container which is as wide as your label should be:

  1. p
  2. {
  3.     margin-left: 150px;
  4. }

3. Then set the label to display as inline block and give it a width and a negative margin that is the same as the margin-left on the container:

  1. label
  2. {
  3.     display: -moz-inline-box;
  4.     display: inline-block;
  5.     width: 150px;
  6.     margin-left:150px;
  7.     vertical-align: top;
  8. }

4. That’s it. Here’s the complete example.

Advantages

Good browser support – I tested it in IE 6-8, Firefox 2-3, Safari 3 and Opera 9.5. The only problem I found is that the label wouldn’t wrap to a second line in Firefox 2 because -moz-inline-box doesn’t support that.

Flexible layout – it works well even if the left and right sides aren’t the same size. Because all of the elements are still in the flow of the document, the container element will just get longer as the children expand.

Simple markup – only extra tag is the container tag. If you use a table or a definition list the label and the field both end up wrapped in another element.

Separates layout from markup – it also doesn’t lock you into a tabular layout. You could easily change the label to sit on the line before the form elements just by changing the CSS without having to touch the markup.

No floated elements – all the elements still belong to the normal flow of the page so you won’t end up with weird clearing problems like you can when you use floats.

Should you just use a table?

I’m undecided. I don’t have a massive problem with using tables because it just works but if I was after something more semantic I think I’d prefer not to use definition lists.

Posted on 27 Jul 08 by Helen Emerson (last updated on 29 Aug 11).
Filed under CSS