If you aim to run a respectable front-end blog there are certain subjects that need to be tackled in order to build up credibility. IE bitching is a popular one, whining about lacking css support in browser X another, but there is one subject that could be considered the holy grail of front-end dev. A subject that most of us fear and dislike, but is still one of the absolute cornerstones of the web today: forms. Be it styling or mark-up, forms remain a fickle element of our job.
The form attribute
Sure I've talked about forms before, but never about the real thing. I wrote a piece on tabular forms and a quick css post on styling equal height forms, but the traditional, full-length, expanded form I've tried to avoid so far. The reason is pretty straight-forward: I've never really felt at ease with the code I've been using. That's not to say that the code I will deliver in this article is absolutely perfect, but at least it shows some interesting connections to other popular web components and it serves as a solid starting point for future improvements.
Learning from the mistakes of others
It's ironic how glaring mistakes can sometimes uncover core truths about particular problems. If you've been doing front-end development long enough, you might remember the days when back-end developers represented a data sheet (typical lists of label/value pairs on detail pages) as a form with the input fields set to disabled. This particular structure made their work a lot easier as they simply had to remove the disabled attributes from the input fields to put the data sheet in edit mode (creating a regular form).
While all of that sounds pretty silly nowadays, there is still something to be said for their line of reasoning. Both data sheet and form are basically the same semantic entity, a form simply being the edit mode of a data sheet. This semantic link between both elements is very real and obvious, so in all fairness this link should be equally apparent in our html code. After all, semantics and structure is what html is all about.
With that in mind, we can adequately define the challenge ahead: come up with a piece of html code that can handle the specificities of both data sheets and forms while keeping the differences to a minimum. html minimalists beware, the result might be a little too verbose to your liking, but we're not aiming for minimalism here.
An old (but popular) way of marking up forms is by using tables. This has actually become somewhat of an accepted practice as most screen reader programs feature specific table-form modes which guides their users through the unsemantic mess. Taking into account the mark-up for data sheets though, tables are simply not a valid option here. We're not going to use tables to mark up label/value pairs, on top of that I'm not very happy with using tables for form mark-up either.
So what's the best way to mark-up label/value pairs then? Well, according to the html5 spec, the dl-dd-dt structure got a recent overhaul to encompass exactly that. It's a reasonably elegant solution for simply data sheets, but if you consider all the extras a regular form needs (user feedback, input hints/help, multiple inputs on a single row) then the drawbacks of the parentless dd-dt pairs become a real pain in the ass. The structure simply doesn't allow us enough flexibility for styling, nor does it provides us with the means of building a logical html structure. Just another reason why I have my reservations about the proposed structure of definition lists.
So all that is left is to build our own data sheet/form structure using divs and some proper classes. Let's give it a shot.
<section class="dataSheet (editable)"> (<form id="formID" action="#" method="#">) <header> (heading/form feedback/required indication) </header> <div class="main"> (label/value pairs) </div> <footer> (crud links/options/submit) </footer> (</form>) </section>
What we have here is a very typical (and generic) component setup. The optional .editable class serves as the form mode toggle, apart from that we use a simple base class for our data sheet component. Note that the form tags are added only in .editable mode. If you're a real purist you could argue that the header shouldn't be wrapped inside the form tag (as it should not hold any input elements), but that would take us a little too far. I left the header inside the form tags as it puts it on the same structural level as the main and footer containers, which feels more natural to me.
The header and footer elements are not always necessary, but they come in pretty handy to separate the label/value pairs from action-related components and/or meta data. The header can be used for headings, form feedback (form error overview or general help) and the classic required indicator explanation. The footer can be used for submit buttons and cancel links in form mode, or for the typical crud options in data mode. And if the design/css doesn't permit it, the crud options can always be added to the header instead.
It's a generic setup I use for many components that have unpredictable and varying degrees of complexity. It's nothing much out of the ordinary so far.
<section class="fieldset"> <h1> ... </h1> </section>
<fieldset> <legend> ... </legend> </fieldset>
As you can see, the tags change according to their context, but the structure remains the same. For ease of styling you can add an extra wrapper div following the legend/h1 which can be used for better cross-browser padding/margin control (fieldsets are notoriously hard to style). Similarly, you can nest a span inside the legend tag for some cross-browser pos:abs magic, but those are all cosmetic changes and have little to do with semantics and/or structure.
This part is of course optional, if your data sheet/form doesn't have any subdivisions then you won't need fieldsets or extra sectioning elements. To make the implementation easier you can also opt to drop the fieldset syntax and always use the sections, that way no extra work is needed when switching between data and edit mode.
wimpy html concessions
<div class="row"> ... <div class="feedback (error) (confirmation)"> ... </div> </div>
The html bit above is something I would rather avoid using, but sadly it's a necessary bit of code if you want to build a little flexibility in your form design. I add it to ease the process of fitting multiple label/value pairs (think first/last name or city/postal code) on one single line. Its semantic value is rather vague (sometimes the pairs are linked, sometimes it's just a matter of saving space), but there's still a practical side to web design that has to be taken into account. It's also the easiest way to provide immediate input feedback, which happens on row-level and not on pair-level. I know this is not ideal, but fitting multiple pairs on one line and providing pair-specific feedback is virtually impossible in most setups.
Label/value pairs - finally!
<div class="spec (inputtype)"> <div class="label"> (<label for="id">)...(</label>) </div> <div class="value"> ... </div> </div>
And so we finally reach the code for our label/value pairs. Notice the seemingly obsolete div.label element, which is used to allow extra hint/help information to go together with the label. You could of course nest the extra info inside the label element, but that way it is always included for screen readers, which can be a bit much at times. At least this way you have the choice, only adding it where needed.
Within the div.value you can either add the actual data or the input controls + all the extra typical form tidbits (calendar overlays and help flyouts) that go with the input element.
Putting all that together, you get a pretty mean piece of html code. From a semantical and structural point of view it's a rock-solid, flexible and reusable code setup, but I'm aware that it brings some overhead and it's rather verbose. If minimalism is your html ideal, this is definitely not for you.
The cool thing though is that it provides a very generic solution to capture the semantics and structure of data sheets and forms with a minimum of impact on the actual code. Not only is it easy to implement, it also makes a lot of sense from a semantical point of view. In its easiest setup the only things that change are the extension class on the root elements, the addition of form tags, the extra label tag and the input elements. To make it a bit more specific there's some extra work to convert fieldsets, but that's about all there is.
Not only will developers be grateful (unless they're using auto-generated CMS form code), it actually makes a lot of semantic sense to work like this. This kind of abstraction always leads to a little overhead, but it's a price I'm willing to pay.