Legends of Style

By John Faulds

UPDATE: following on from comments by Thierry Koblenz, I have written an update to this article. The techniques described below will still work in different browsers, but the new article explains how it can be achieved with a little less mark-up.

It's a well-established fact that achieving cross browser consistency when styling form controls is an “exercise in futility”. And one of those elements that just won't play ball is the <legend> tag.

A legend is like a caption for a <fieldset> and a fieldset is used for grouping related form controls. You don't have to use either in your forms, but if your forms are lengthy, using fieldsets can make your forms more usable by dividing them into smaller, manageable pieces, and if you use fieldsets, you must have a legend.[1]A legend is required when using a strict HTML 4.0 doctype. Pages using strict XHTML doctypes will validate without a legend, but from an accessibility point of view, it is still recommended that you have one.

The problem

Legends work just fine if you're not doing much styling of your form. Their default state is to display along the top border of the fieldset and indented from the left slightly, as can be seen below. (The screenshot is from Firefox 2.0.0.1, Opera 9.02 and IE6 - IE7 is identical to IE6.)

Screenshots of unstyled fieldsets with legends

Problems occur when you try to use background-colors on your form or fieldset or if you try to position the legend in any way other than the default. Internet Explorer particularly has a problem with background-colors:

Screenshots of fieldsets with legends with a background-color applied to the fieldset

The solution

So what do you do if you want a more customised look to your forms? One solution is to add extra tags around both the fieldset and legend content and style those instead.

Unlike most form elements, fieldsets and legends are simply containers for content and don't play any part in the processing of form information, so if we use other elements to mimic their display properties, no real harm is done either to the script that processes the submitted form or to the visitor to your site.

OK, so they get a bit of additional markup, but it's not a lot (as we'll see next) and it in no way reduces the usability and accessibility of the form. In fact, as a lot of people disregard fieldsets and legends and their associated accessibility and usability features due to their stubborn resistance to styling, the additional markup means we're not sacrificing those aspects any more for the design.

So how's it actually done? In place of fieldset and legend we're going to use generic <div> and <span> respectively. We use a span inside the legend because legends can only contain inline content. We remove all the styling from the fieldset and legend and instead apply it to the div and the span.

The black sheep

The extra div wouldn't be necessary if it weren't for one particular browser. And no, it's not IE this time: it's Firefox. As evidenced on Bugzilla, how to style legends with the Gecko rendering engine is a bit of a thorny issue. I was suprised, however, when first looking into this issue that Firefox not only has trouble with the legend when trying to use position: absolute, but also when the same is applied to a span inside a legend.

The problem is highlighted in these examples. The examples of both fieldset/legend pairs look fine in IE6 & 7 and Opera 9 (Opera 8.5 has a little trouble with the positioning in the first example too), but in Firefox 2 and 1.5, the legend in Example 1 seems to take it's top and left coordinates from the first element in the form after the legend (a label) and not from the fieldset which has position: relative on it to establish a new positioning context.

It's only to get the same result in Firefox that we actually need to add the extra div around the fieldset and move all the styling from the fieldset to the div (Example 2). The div also needs to wrap the fieldset and not the other way around because then the HTML would be invalid.

The examples

Having established what needs to be done to bring consistency to the styling between browsers, let's look at a few different examples of how the legend can be positioned.

Example 1 shows the legend inset within the frame of the fieldset (or the div that looks like a fieldset) that holds it.

fieldset { border: none; }

.fieldset {
  width: 30em;
  position: relative;
  padding: 2.5em 1em 0.5em 1em;
  border: 1px solid #000;
  background: #F8F8F8;
  font-size: 90%;
}

legend span {
  position: absolute;
  width: 29em;
  top: 0.5em; left: 1em;
  color: #000;
  font-weight: bold;
}

I’ve given the div that holds the fieldset a class of fieldset and applied the background-color and border to that instead of the fieldset which has its default border turned off. It’s also given position: relative to establish a new positioning context which means the legend span will be placed in relation to it.

The span inside the legend is given a width because Opera 8.5 won’t display the text in a single line otherwise. It’s also useful if you want to give it a background and border as in the second example. It’s then positioned absolutely and given the appropriate top and left values to place it where it’s wanted.

Example 2 shows the legend sitting in the middle of the top border, similar to its default display. The difference is, of course, that we don’t get the problem with the background of the fieldset showing above its top border in IE.

.two legend span {
  top: -0.75em; left: 1em;
  padding: 0 0.2em;
  background: #FFF;
  border: 1px solid #000;
}

.fieldset.two { padding-top: 1.5em }

It’s given a background and border and a bit of extra padding and a negative top value to pull it up over the top border. The containing div is given a smaller padding-top in this case because it doesn’t need to make as much room for the legend in this example.

Example 3 takes it one step further and moves the legend completely above the fieldset.

.three legend span { top: -2em; left: 0; }

.fieldset.three { padding-top: 0 }

This example doesn’t need any border, background etc., and the container doesn’t need any top padding at all this time.

Example 4 pushes the legend over into the empty space at the top right corner of the fieldset.

.four legend span {
  top: 0.5em; left: 26em;
  width: 5em;
  text-align: right;
}

I’ve given it a smaller width because I wanted it to run over several lines and given it a large left value to place it where I want.

Example 5 is probably something you would hardly ever do, particularly as this won’t work in anything but IE at the moment. That’s because it uses the word-wrap: break-word property which is only supported by IE (although it is in the working draft for CSS3).

.five legend span {
  top: 0; left: -1.5em;
  width: 0.5em;
  text-align: right;
  word-wrap: break-word;
  line-height: 1;
  text-transform: uppercase;
}

.fieldset.five { margin-left: 1.5em; padding-top: 0 }

I won’t go into too much detail about this last example. Just check it out in IE. ;)