June 23, 2005

CSS Distress

I'm a big fan of CSS, and CSS positioning. I've always heard and believed the mantra "Use CSS for positioning; use tables only for tabular data". It seemed wise to my young and innocent ears. However, I've recently been having some doubts, brought on by considering a common layout case.

Here's my scenario. I want to have a series of labelled widgets, e.g. for a "Preferences" page. Quick ASCII art diagram:

Foo:          [ Freeform Text   ]
Bar Setting:  [ Sometimes Bar |V]
Baz?          ( ) Yes, Baz
              (.) No, don't Baz
Quux:         [X]

(No, you don't need to point out the number of UI design principles broken in this diagram. It's an example.)

Doing this with CSS would involve something like two divs, one for the left side and one for the right, either floated or absolutely positioned, and a lot of messing about with vertical margins and padding trying to get the widgets to line up exactly with their descriptions at various font sizes. And, even if I managed it, is it the right thing to do? Would a screen reader read all the descriptions and then all the widgets?

Should I (whisper it) be using tables for this, even though it's not really tabular data? Or is it?

Posted by gerv at June 23, 2005 10:43 AM
Comments

The 'right answer' is to use display:table et al for the presentation layer, but not to use <table> for the semantics.

Malcolm

Posted by: Malcolm at June 23, 2005 10:59 AM

Of course, I should also mention that no browser actually supports that properly, so the practical way to achieve the effect you're looking for is probably display:block, float:left, and an explicit clear at the end of each row (or at the start of the next row, perhaps).

Posted by: Malcolm at June 23, 2005 11:13 AM

See how I did this on my sunrise calculation page.

Although it's in german, you'll have no problems reading the CSS!

Posted by: Beat Bolli at June 23, 2005 11:22 AM

Hey hey!

Something like this is very possible, just check Quirksmode:
http://www.quirksmode.org/css/forms.html

I'm using it to great effect. Quirksmode is pretty cool, and there's a meeting this Sunday in Amsterdam.

Posted by: Alper at June 23, 2005 11:27 AM

This may interest you: http://www.quirksmode.org/css/forms.html

Posted by: marcoos at June 23, 2005 11:27 AM
<form>
<div class="formRow">
<label>Foo:</label><textarea></textarea>
</div>
</form>

This sort of thing gives you a lot of power. Turn the label display:block and float it left, give it a width and you've got a left hand "column". Style the formRow class and you get separation between each label/formElement set.

For more flexibility, use multiple classes on the div (class="formRow address") to style the label/formElement sets individually.

Posted by: Colin Ramsay at June 23, 2005 11:40 AM

As the others have mentioned, look at the method described on quirksmode.

Give a width to [label] tags, and float them; ditto for [input]s (which implies display:block;). I've used a version of this method for an intranet app; the CSS for a [label] looks like this:


float: left;
clear: both;
width: 7em;
text-align: right;
margin: 0.3em 1em 0 0;
padding: 1px 2px 1px 0;
background: #eed;
color: #000;

If you want to get clever, you can style [fieldset]s and [legend]s as well (margin, border, padding, clear). This lets your logical groupings be your presentational groupings as well.

I also had a fieldset.halfwidth class (heresy! presentation leaking into HTML attributes!) which let me float two smaller [fieldset]s in a [form] side-by-side, using:

float:left;
clear:none;
width:47%;

The big win is that small-screen users don't have the forms scrolling off horizontally as much (first, the [input]s wrap underneath the [label]s)

Posted by: Mark Tyndall at June 23, 2005 11:57 AM

I use a definition list for this. The dt as the label and the dd as the control. Positioning this is not too hard: http://browservulsel.blogspot.com/2005/04/semantic-html-forms-2.html

Posted by: Jasper at June 23, 2005 12:06 PM

Thouh shal no use Tables. Thats why god invented and ;)

Posted by: TheOtherGuy at June 23, 2005 12:20 PM

In my opinion, the kind of fill-in form you use is a transposed table, rows are shown as columns and columns are shown as rows, with only 1 item to show in your data collection; think of headers as labels.
If you would have multiple 'preferences' (or customers, or invoices, or whatever) rows to edit, you would also show them in a (regular) table.
With this approach, you can use both table and CSS powers.
Tables give you proper alignment, CSS give you layout flexibility:

<table class="form">
<tr>
<th>Foo</th>
<td><input type="text" name="foo" id="foo" /></td>
</tr>
...
<tr>
<td></td>
<td><input type="submit" text="submit" /></td>
</tr>
</table>

and something like this in your stylesheet:
table.form {
width: 500px;
}
table.form th {
background-color: #999999;
text-align: right;
width: 30%;
}
table.form td {
background-color: #99cc99;
}

I've strugled with div, span, float, clear, label, etc before, but no solution gave me the result (both in layout as in semantics) that I was looking for. This comes pretty close to what's the perfect solution *for me*

Posted by: anthoro at June 23, 2005 12:54 PM

This is tabular data Gerv... If you really want to try a CSS-based thing, go and do it, but, and that's with my old CSS WG member hat on, I'm telling you: use a table.

Posted by: Daniel Glazman at June 23, 2005 1:29 PM

There is no need to use divs, or to float the form elements (as in the quirksmode example).

Using the same html as quirksmode (this is the most semanically correct IMO) the following style rule takes care of everyting:

label {
display: block;
width: 6em;
float: left;
text-align: right;
margin-right: 1em;
}

This requires that you prepend all radio buttons except the first with an empty label. It might be a good idea to use double br:s except between the radio buttons to get a better look.

If the heiht of the label is significantly higher than the input elements, set the clear property on the br (it might be a good idea to do anyway, just in case).

Set the line-height property on the form element to increase spacing between the form rows.

Posted by: Fredrik at June 23, 2005 1:38 PM

What a great blog post! Thanks Gerv.

I've been trying to find the best way of doing this for ages, and its been really interesting to see all these peoples different solutions.

Nice, I've enough ammo to now to take on this problem (Hopefully you do to!) :)

Mac.

Posted by: Mark McDonnell at June 23, 2005 1:44 PM

For what it's worth, it looks like tabular data to me. Some of these answers, however, look like they might belong in the "CSS Tips" section over at Devmo. ;)

Posted by: Deb Richardson at June 23, 2005 3:14 PM

Use table. It's more generic and browser safe. You don't need to decide the first column width and easy to control alignment. If you have this kind of preference form in many places, set style to table.

For example :
<style>
.myTable {background-color:lightgrey;}
</style>

<table class="myTable">.....</table>

It's better to put generic style into css file. Then you can control layout easily.
That's beauty of style sheet.

If you worry about accessibility, you can add
<LABEL for="Freeform_Text_field">Foo</LABEL>

Posted by: alex at June 23, 2005 3:50 PM

I do not agree that labels and controls are tabular. If you do decide to use a table you don't need a class. You could use "form table" as a CSS selector, which keeps you html cleaner. My defenition list also doesn't have a class:

form dt {
position: absolute;
z-index: 2;
}

form dd {
position: relative;
padding-left: 100px;
}

Posted by: Jasper at June 23, 2005 4:10 PM

Quirckmode's way is very nice (and correct) but you get into trouble when stuff gets more complex.
To quote from Gervase's sample, "Yes, Baz" also needs to be inside a label (to properly enable it's "for" property). And what if you need an input type=text after that label, properly aligned and "Mph" behind that.
Aligning all elements for all situations cross-browser can become quite daunting.

Also, <legend> is only partly stylable, it would be nice if at least firefox would fix that.

Posted by: paul at June 23, 2005 4:17 PM

That's definitely tabular data. Imagining column headings named "Option Name" and "Option Value" makes it quite obvious that you're intentionally putting data in rows and columns. Using a table is the structurally correct thing to do in this case.

Posted by: Joel Bruick at June 23, 2005 4:54 PM

Alper and Beat Bolli,

did you guys ever print out your pages in Firefox? Look no good to me.

Posted by: cyfer at June 23, 2005 6:03 PM

I've written a very similar page recently for an intranet app. After some internal debate I decided that tables were the cleaner solution - I hadn't thought of it in the same way as Joel Bruick but it's a good point. Sites that dramatically overuse tables for layout would be able to name the columns.

Posted by: JonL at June 23, 2005 7:20 PM

Hi!

The suggestion I like the most so far is the dd/dt solution. Tables always generate more clutter (tbody, tr). I'd like to make a suggestion too.

Go for the labels, because they are driving the layout. Labels can be "clear:both" and "float:left", which ensures that "no two labels are on the same line". This is already powerful, especially when textareas are involved. Fields benefit from being static (i.e. not floating).

I like to give a padding-left to the container div instead of setting margin-left for the inputs, because it makes it easier to get a layout that fits upon window resize.

Finally, a little markup for nested fieldsets with different alignments is worth a lot, but you should rarely need it, if ever. I'd rather redesign the form. If you do need, say, a date input with three labeled fields, I believe you would like to define a div (or something else) for calendar entries (if you give nice error messages with highlighted failed inputs, you'll like it later). Besides, since the fields are actually one "input", you can easily convince yourself that it isn't really markup for presentation. : )

Posted by: Tiago Silveira at June 23, 2005 8:41 PM

I just use CSS. The proper container for a group of form elements isn't a table, it's a fieldset. If you then want to nest a table inside that then go for it, but the markup payload increases massively, and you start heading back to the bad old days. I don't like the dl idea either - you're not defining a term and a definition initally, you're asking for input. My general CSS looks something like:

label {
  display: block;
  width: 30%;
  float: left;
  clear: left;
}
Posted by: Robin at June 23, 2005 10:13 PM

I remember reading something a month or so back on some blog about styling forms elemts with css. I can't find it again now but this may give you some ideas.

Posted by: Sam Hasler at June 24, 2005 12:37 AM

In XUL you would use GRID. GRID in HTML is a table. As Daniel stated: use a table

Posted by: Pete at June 24, 2005 1:29 AM

CSS-Only Forms has been hanging around del.icio.us/popular for the last few days. It seems like a fairly comprehensive look at the issue.

(Although I do tend to agree with those who say that it's tabular data.)

Posted by: David Lynch at June 24, 2005 7:07 AM

To those who do not think a table is appropriate, can you tell me when it is?

Posted by: Jon Dowland at June 24, 2005 10:16 AM
To those who do not think a table is appropriate, can you tell me when it is?

When displaying data.

Posted by: Colin Ramsay at June 24, 2005 10:48 AM

Colin Ramsay's solution works very well. Go to my blog (example http://dascritch.net/blog.php/2005/06/22/172-un-grand-pouf#co ) go to the bottom, clic "Ajouter un commentaire". Dotclear CMS's templates use it for two years with style...

Posted by: Da Scritch at June 24, 2005 12:54 PM

Using a table is much more intuitive, browser safe and (if only one table level is used) still good human readable to me. I see no "killer argument" pro css. Well, beisde all those fuzzy "abstraction of content and design" or "screen reader compatibility" arguments that mostly don't count a bit in real world...

Posted by: mikx at June 24, 2005 1:06 PM

I just reread Malcom's fist comment:
"the 'right answer' is to use display:table et al..."

But this would imply that you need something like this
bla...

Is that assumption correct? It doesn't look very intutive for me.

Posted by: Pete at June 24, 2005 5:28 PM

I've been using tables for this kind of thing for years. I tried to do it with CSS just once and I wanted to kill myself afterwards. There's nothing wrong with putting form data in tables.

Posted by: Brooks at June 24, 2005 10:11 PM

Sure, but labels and inputs aren't data. I deal with data tables all day in my job and I'm happy to use them for such, but it just doesn't make semantic sense in this case. Whether or not you want to go with the defined semantics for html elements is a choice you'd want to make yourself, but seeing as it's easy to lay forms out with CSS I know what I'm going to carry on doing.

Posted by: Robin at June 24, 2005 11:47 PM

At the risk of repeating what others have said, and disagreeing with what even more people have said, I'd be inclined to say that you're looking at tabular data here. Tables can be used for related datasets. For example, it would make perfect sense to use a table for a list of cities and their corresponding populations. Why not, therefore, use a table for a set of questions and their corresponding answers.

Posted by: Ali Ebrahim at June 25, 2005 5:51 AM

Just call it interactive tabular data and wait for CSS to catch up with the real world ;)

Posted by: Adrian D. at June 25, 2005 1:03 PM

FWIW, metooing to it being tabular data. I use tables for lining up labels and fields in cases like that.

It seems to me the "all tables are evil" propaganda has succeeded too well.

Posted by: Henri Sivonen at June 25, 2005 7:41 PM