If you’ve ever tried to make an HTML form look like the fancy PSD mockup that your graphic designer threw together, you know how much hate forms deserve. Forms are an integral part of the web, yet making them semantic, accessible, and visually appealing across browsers can be a major bitch.
There’s lots of information scattered about the interwebs about arm-wrestling forms into submission, but I recently ran across an issue that has not, so far as I know, been adequately discussed. That topic is legends.
No, not like the legend of Jeffrey Zeldman and his big blue ox, although that’s a good one; I’m talking about the legend element that attaches to fieldsets in order to let your visitor know what the heck this group of form controls they’re supposed to fill out pertains to. Legends, it turns out, are some of the most annoying elements to style you can find. Not only do they by default have some bizarre formatting applied to them by every browser out there (usually in slightly different ways), but normal styling rules don’t apply. Display: block
, anyone? Yeah, ain’t happening.
Recently I was asked to style a really nicely laid out form; it had everything lined up down the left side, clearly labeled sections, the works. Except that the headings for each section were occasionally long enough to wrap, and were displayed with many of the same rules as the H3’s on the page. Easy enough, I figured, I’ll just stick display: block
and some margins on them.
Which is when I discovered that display: block
has no effect on legends in any browser (any browser worth testing in, that is, which means IE 6, IE 7, Firefox 2, Firefox 3, and Safari for my company). Legends with lots of text in them tend to stretch themselves or, worse, their parent elements out to keep everything on the same line, and even if they’re supposedly block-level elements with a margin they’re still treated as inline. Not cool. Fortunately, my latent CSS superpowers were activated when I started swearing like a sailor (wish I had a more socially acceptable trigger, but there you go) and I pounded out some CSS that:
- Forces long legends to wrap in IE, Firefox, and Safari
- Encourages legends to act like they know what
display: block
is all about
The code looks like this (or see it in action):
HTML
<fieldset>
<legend><span>Legend text</span></legend>
</fieldset>
CSS
legend {
margin: 0 0 15px;
float: left;
white-space: normal;
*margin-left: -7px;
}
legend span {
width: 400px;
display: block;
}
Seem weird? That’s because it is. Here’s the reasons for the insanity:
If you’re like me, you’ll want a margin on your block level elements; in order to get your margin recognized in Firefox 2, you have to put it on the legend element. Of course, margins won’t be recognized on inline elements or legends, but since display: block
does squat for a legend you have to float the legend left. For the full display: block
effect, of course, there should be a width on the legend, but since Firefox will collapse the legend’s width down to its content anyway, I left it off.
Instead, the semantically meaningless span gets the width and display: block
. The span not only forces Firefox to bump the legend’s width out the correct amount, but it also is necessary for long legends to wrap in some browsers. I’m using a pixel width mainly because IE 6 requires a different pixel width (more on this later). Although in a perfect world your legends would not have anything in them other than the legend text, we don’t live in a perfect world.
Adding white-space: normal
to either the span or the legend will fix the wrapping problems in Firefox 3 (I chose to attach it to the legend in the hopes that in fifty years or so I can rid myself of that span with less effort).
The *margin-left: -7px
line is a hack to fix an IE-specific problem. Even with no padding, margin, and left: 0px
IE will still indent your legends seven pixels. The star-property hack targets both IE 6 and 7 (while being ignored by sane browsers) and bumps the legend back over where it should be. I’m using the star-property hack here because it makes the source a lot less verbose; however, you should probably migrate that rule into an IE-specific stylesheet behind a conditional comment.
There is also one more IE-specific adjustment that you have to make thanks to the seven pixel indent. When calculating its box model, IE 6 will determine the width of the legend and add the seven pixel indent before shifting the legend to the left. Long and short of it is that the parent element will be bumped out seven pixels wider than it should be, so you’ll need to give the span a width seven pixels less than your target width for IE 6. For instance, to make the code above work in IE 6 I would add an IE 6 conditional comment with legend span { width: 393px; }
inside somewhere.
And that’s it! You will, of course, want to be using an intelligent CSS reset (I recommend Eric Meyer’s CSS reset), but otherwise the above CSS should give you a legend element that acts like a block-level element instead of some weird border-loving pseudo-element with wrapping issues.
There are some further gotchas to be aware of if you start playing around with legends. IE by default colors all legends blue, for instance, and if you want the legend to overlay the fieldset border (or not) you’ll need to go about things slightly differently. However, if you’ve been frustrated by the inability to get your legends to behave like display: block
means something, hopefully this snippet will help you out.
As is usually the case, I discovered this particular combination of HTML and CSS by taking previous solutions, mixing them into a putrescent green cocktail, and force-feeding it to my browsers, tweaking the constituent ingredients until they were saying “Mmm, tasty!” instead of puking a bunch of crap all over my lovely webpage. Specifically, John Faulds provided some basic concepts and examples for styling legends and Stephanie Sullivan wrote up the strategies for encouraging legend wrapping in Firefox 2 and 3. Thanks, guys; you rock.
If you want to see the code in action with various permutations for More Pretty, check out this page o’ examples.