{"rowid": 127, "title": "Showing Good Form", "contents": "Earlier this year, I forget exactly when (it\u2019s been a good year), I was building a client site that needed widgets which look like this (designed, incidentally, by my erstwhile writing partner, Cameron Adams):\n\n\n\nBuilding this was a challenge not just in CSS, but in choosing the proper markup \u2013 how should such a widget be constructed?\n\nMmm \u2026 markup\n\nIt seemed to me there were two key issues to deal with:\n\n\n\tThe function of the interface is to input information, so semantically this is a form, therefore we have to find a way of building it using form elements: fieldset, legend, label and input\n\tWe can\u2019t use a table for layout, even though that would clearly be the easiest solution!\n\n\nAbusing tables for layout is never good \u2013 physical layout is not what table semantics mean. But even if this data can be described as a table, we shouldn\u2019t mix forms markup with non-forms markup, because of the behavioral impact this can have on a screen reader:\n\nTo take a prominent example, the screen reader JAWS has a mode specifically for interacting with forms (cunningly known as \u201cforms mode\u201d). When running in this mode its output only includes relevant elements \u2013 legends, labels and form controls themselves. Any other kind of markup \u2013 like text in a previous table cell, a paragraph or list in between \u2013 is simply ignored. The user in this situation would have to switch continually in and out of forms mode to hear all the content. (For more about this issue and some test examples, there\u2019s a thread at accessify forum which wanders in that direction.)\n\nOne further issue for screen reader users is implied by the design: the input fields are associated together in rows and columns, and a sighted user can visually scan across and down to make those associations; but a blind user can\u2019t do that. For such a user the row and column header data will need to be there at every axis; in other words, the layout should be more like this:\n\n\n\nAnd constructed with appropriate semantic markup to convey those relationships. By this point the selection of elements seems pretty clear: each row is a fieldset, the row header is a legend, and each column header is a label, associated with an input.\n\nHere\u2019s what that form looks like with no CSS:\n\n\n\nAnd here\u2019s some markup for the first row (with most of the attributes removed just to keep this example succinct):\n\n
\n\t\n\t\tMatch points\n\t\n\t\n\t\n\t\n\t\n
\n\nThe span inside each legend is because legend elements are highly resistant to styling! Indeed they\u2019re one of the most stubborn elements in the browsers\u2019 vocabulary. Oh man \u2026 how I wrestled with the buggers \u2026 until this obvious alternative occurred to me! So the legend element itself is just a container, while all the styling is on the inner span.\n\nOh yeah, there was some CSS too\n\nI\u2019m not gonna dwell too much on the CSS it took to make this work \u2013 this is a short article, and it\u2019s all there in the demo [demo page, style sheet]\n\nBut I do want to touch on the most interesting bit \u2013 where we get from a layout with headers on every row, to one where only the top row has headers \u2013 or at least, so it appears to graphical browsers. For screen readers, as we noted, we need those headers on every row, so we should employ some cunning CSS to partly negate their visual presence, without removing them from the output.\n\nThe core styling for each label span is like this:\n\nlabel span\n{\n\tdisplay:block;\n\tpadding:5px;\n\tline-height:1em;\n\tbackground:#423221;\n\tcolor:#fff;\n\tfont-weight:bold;\n}\n\nBut in the rows below the header they have these additional rules:\n\nfieldset.body label span\n{\n\tpadding:0 5px;\n\tline-height:0;\n\tposition:relative;\n\ttop:-10000em;\n}\n\nThe rendered width of the element is preserved, ensuring that the surrounding label is still the same width as the one in the header row above, and hence a unified column width is preserved all the way down. But the element effectively has no height, and so it\u2019s effectively invisible. The styling is done this way, rather than just setting the height to zero and using overflow:hidden, because to do that would expose an unrelated quirk with another popular screen reader! (It would hide the output from Window Eyes, as shown in this test example at access matters.)\n\nThe finished widget\n\nIt\u2019s an intricate beast allright! But after all that we do indeed get the widget we want:\n\n\n\tDemo page\n\tStyle sheet\n\n\nIt\u2019s not perfect, most notably because the legends have to have a fixed width; this can be in em to allow for text scaling, but it still doesn\u2019t allow the content to break into multiple lines. It also doesn\u2019t look quite right in Safari; and some CSS hacking was needed to make it look right in IE6 and IE7.\n\nStill it worked well enough for the purpose, and satisfied the client completely. And most of all it re-assured me in my faith \u2013 that there\u2019s never any need to abuse tables for layout. (Unless of course you think this content is a table anyway, but that\u2019s another story!)", "year": "2006", "author": "James Edwards", "author_slug": "jamesedwards", "published": "2006-12-11T00:00:00+00:00", "url": "https://24ways.org/2006/showing-good-form/", "topic": "ux"}