July 1, 2005

Refactoring and CSS

Cascading Style Sheets (CSS) have a reputation for being hard to learn and challenging to use well. One reason, I think, is that declarative languages like CSS seem to be hard to refactor.

Let's suppose, for example, that we're tweaking a layout to add a feature to a corporate home page. To make things fit right, we adjust some typography:

body#newFeature div.adbox h2
margin-bottom: 12px;}

Little ad hoc adjustments like this let designers put stuff where it belongs. Before you know it, you've got dozens of little adjustments.

Now, does this particular adjustment belong here? Or could it be moved up to adbox, or to newFeature? Perhaps instead of changing the bottom margin of H2 we should be looking at the top margin of something else. There are lots of places we could put the adjustment; which is right?

In practice, moving the rule is scary because you might break a layout in grotesque but unpredictable ways. Unit testing makes sense, but the layout you break might well be one you're planning to write tomorrow. When the layout does break, it can also be challenging to distinguish the cause:

I often have questions I'd like to ask the CSS interpreter. "Why isn't this floating?" "What makes you think that paragraph should be Verdana?" In a procedural language, I could fire up the debugger and set a breakpoint. In a declarative language like CSS, what can you do?

Update: Matt Hanlon recommends Xylescope.

Update: Fazal Majid says the Mozilla/Firefox DOM browser 'makes debugging CSS a breeze. You can ask it to display the computed CSS style for a DOM node, or the style rules that were used to derive the effective style.'