The Problem
At my job we don’t have a dedicated CSS/web design guy. Programmers do all of the design work themselves. That’s not to say that such a situation is unnatural, in desktop app development it is the norm. Programmers tend to approach any presentation technology as something that should be able to express their will exactly. HTML & CSS are notoriously bad at this because they are not a true presentation framework. HTML is a semantic document markup language and CSS can be overridden by browser specific behavior and user settings. That is as it was intended, the User Agent is in control of the final rendering, not the author. So a typical programmer reaction is to be even more specific to force the User Agent to display what we want. This might include using per-browser style sheets, absolute positioning of elements or tables for non tabular data. This tends to result in more markup, more CSS, more testing and more bugs to fix. The programmers gut reaction is at odds with the reality of the web.
So my goal is to give the programmers something they can live with that achieves these goals:
- Reduce the number of cross browser bugs and testing
- Minimize the amount of custom CSS we write for each new page
- Have a single style sheet for all the browsers we support
- Allow developers to build layouts that I haven’t thought of
- Be ready to support mobile devices
- Make the HTML and CSS maintainable
My first attempt was to try and get the team to adopt Yahoo’s YUI CSS for layouts. Adoption by the team didn’t go so well. It was hard to get the layout you wanted 100% of the time. Nesting of layouts would sometimes introduce bugs. No one liked the cryptic style names. So I went looking for a replacement and found Nichole Sullivan’s ‘Object Oriented CSS‘ framework. The code is much simpler than the yahoo framework and it’s a much closer fit for how we work.
The ideas behind Object Oriented CSS make a lot of sense but the name make almost no sense to me. I think it most closely resembles Attribute-Oriented Programming. You place attributes (CSS classes) on elements (Objects) in combination to achieve some desired effect. In programming Attributes can be used to mark Objects for later use by other code that acts on those objects. They can also describe some quality about an Object, like it being serializable or a database entity. Attributes are written in such a way that they combine gracefully and interact only when necessary. In programming an Attribute can have arguments. In CSS the arguments are the element on which the attribute is acting and the other attributes applied to that element. So now we have a model for thinking about the problem.
CSS attributes combine to produce a wide variety of effects from comparatively little CSS code. To achieve a total effect you combine attributes together on an element. If the exact attribute you need is not available you can use the ones that do exist plus some small amount of style information to produce the effect.
This gives us a way of looking at CSS code and accessing it like we do other kinds of code. We can look for potential refactorings like we do in other kinds of code. Its just a way of looking at the problem.
Once I got my head around this I realized that I couldn’t just take OOCSS to the programmers because there were things about it that needed to change. It needed to become even more attribute oriented and it needed to be more consistent.
Sample Refactoring
Lets take an example from the OOCSS framework and improve on it by thinking in an Attribute Oriented way. In OOCSS there is a debugging style sheet that adds background colors to block so you can visualize your layout. This is a great idea but using it is awkward. First you have to include a separate style sheet in the document. In a template system that means going and finding a separate template with the Head tag and modifying it. Once that’s done debugging is on for ALL layouts in the document, not just the two boxes you want to debug. Also debugging only works for OOCSS layouts, you cant debug some arbitrary elements.
So, lets define a ‘.debug’ attribute the we can use to mark elements we want to debug. For the layout classes it can have special behavior (Attribute behave differently depending on their input, remember?). For non grid elements we can also set a background so we can use ‘.debug’ on anything.
/* all elements get a gray background as the default behavior*/
.debug {background-color:#e2e2e2;}
/* Extend the .debug attribute with special behavior for layouts: */
.size1of1.debug {background-color:pink;}
.size1of2.debug {background-color:red;}
.size1of3.debug {background-color:orange;}
.size2of3.debug {background-color:yellow;}
.size1of4.debug {background-color:lime;}
.size3of4.debug {background-color:green;}
.size1of5.debug {background-color:aqua;}
.size2of5.debug {background-color:blue;}
.size3of5.debug {background-color:purple;}
.size4of5.debug {background-color:magenta;}
Now we have a debug attribute that works anywhere and we can quickly apply it to anything we want to debug.
Names Matter
The fixed size grids in OOCSS are focused on laying out the main chunks of the page. This is kind of unfortunate because there are a lot of places where they come in handy. This snipet comes from OOCSS:
/* ====== Columns ====== */
.main{overflow: hidden;_overflow:visible;_zoom:1;}
.leftCol{float:left; width:250px;_margin-right:-3px;}
.rightCol{float:right; width: 300px;_margin-left:-3px;}
/* extend columns to allow for common column widths */
.gMail{width:160px;}
.gCal{width:180px;}
.yahoo{width:240px;}
.myYahoo{width:300px;}
Here is my proposed re-write:
/* Fixed Width Layouts */
.layout-fixed {clear: both;} /* facilitate layout stacking */
.col-main {overflow:hidden; _overflow:visible; _zoom:1;}
.col-left {float:left; width:30px; _margin-right:-3px;}
.col-right {float:right; width:30px; _margin-left:-3px;}
/* Fixed pixel widths */
.width-160 {width:160px;}
.width-180 {width:180px;}
.width-240 {width:240px;}
.width-300 {width:300px;}
/* + more as you require... */
This is very subtle change. The ‘.col-left’ and ‘.col-right’ attributes now set the same default width. Not setting the width is an option but its better for the column to have some width so the programmer gets some kind of feedback that the attribute is working (default attribute behavior). Setting the same width on .col-left and .col-right promotes a feel of consistency. We programmers like it when things behave consistently. Changing the names of the specific width styles to “.width-NNN” has some nice effects. It’s easier to read in the code. ‘.width-300′ is more likely to be re-used than ‘.myYahoo’ because its easier to understand what it claims to do. It’s not clear that its safe to use ‘.myYahoo’ in other situations, it might have side effects or set other styles that you don’t want. ‘.width-300′ is a specific attribute which claims to do only one thing. Finally the ‘.width-NNN’ sets up a convention. Programmers will be encouraged to try ‘.width-500′ before they start writing a custom style for that width.
So the source for a two column layout would go from this in OOCSS:
<div>
<div class="leftCol myYahoo"></div>
<div class="main"></div>
</div>
To this with my changes:
<div class="layout-fixed">
<div class="col-left width-300"></div>
<div class="col-main last"></div>
</div>
The programmers in the crowd will see this as a big win for readability. The designers point out that this is more verbose and size matters. Designers, size wont matter at all if you cant get the programmers you work with to adopt the framework. If you get this into common usage then later you can do a global re-name to smaller names. Honestly for most sites, size does not matter that much. Programmers will implement zip compression on the web server before they do a re-name. Readability and maintainability are more important than page size. If page size really matters that much you can optimize it later. You cant optimize it later if the CSS is an inconsistent mess.
Integrating Fixed Width and Percentage Width Layouts
The naming for fixed width and percentage width layouts in OOCSS is very different. Attributes that have similar effects or usage usually have similar names. Certainly the declarations that determine the column widths could follow the ‘width-XXX’ convention.
.width-auto {width:auto;}
.width-1of1 {width:100%;} /* set 100% width outside of layouts */
.col.width-1of1 {float:none; width: auto;} /* this doesn't set width=100% so make this context sensitive */
.width-1of2 {width:50%;}
.width-1of3 {width:33.33333%;}
.width-2of3 {width:66.66666%;}
.width-1of4 {width:25%;}
.width-3of4 {width:75%;}
.width-1of5 {width:20%;}
.width-2of5 {width:40%;}
.width-3of5 {width:60%;}
.width-4of5 {width:80%;}
This leaves us with the core of the percentage width grid system. Again from OOCSS (grid.css):
.line, .lastUnit {overflow:hidden; _overflow:visible; _zoom:1;}
.unit {float:left; _zoom:1;}
.lastUnit {float:none; _position:relative; _left:-3px; _margin-right:-3px; width:auto;}
It doesn’t look much like the fixed width system. Lets say that we have a layout with two columns that are split up 1/3 to 2/3. The HTML looks like this:
<div class="line">
<div class="unit size1of3"></div>
<div class="unit lastUnit size2of3"></div>
</div>
Now lets say there is a requirements change and the developer needs to alter this layout. The new requirement is for the left column to be 300px wide and the right column is flexible. The code using OOCSS would look like this:
<div>
<div class="leftCol myYahoo"></div>
<div class="main"></div>
</div>
In addition to changing the width attribute you need to remember to remove the ‘lastUnit’ class and to change things from ‘unit’ to ‘col-’. We can make the ‘last’ marker harmless in the case where its used with fixed-width layouts so forgetting to remove it will not harm the presentation. Then we can tell developers to always include it. It wont be necessary in one case but it will be consistent and that will probably reduce frustration. Also lets name the attribute ‘last’ so that we can use this kind of marker attribute in other places as a convention, like the last row in a table or the last button in a button strip. In programming some attributes have no effect unless they are used in conjunction with another attribute or placed on a particular type. ‘last’ may get used widely but it wont have any default behavour. Its important to consider this when building your framework. If something has global effects its harder to make it have local effects later.
Then there is the ‘.line’ declaration. You cant forget to add that. I think it’s better if there is a declaration in both layout types so the programmer has to say what they mean, semantically, even if its not necessary. As it turns out it is necessary to make fixed width and percentage width layouts stack ontop of each other. Finally we can re-name ‘.unit’ to ‘.col’ so it looks more like the fixed width versions.
.layout-percent {clear:both;} /* facilitate layout stacking */
.layout-percent, .col.last {overflow:hidden; _overflow:visible; _zoom:1;}
.col {float:left; _zoom:1;}
/* make .last only apply to columns in percentage width layouts */
.col.last {float:none;_position:relative; _left:-3px; _margin-right: -3px; _width:auto;}
And now we have the two versions of the source again. First the percentage width version:
<div class="layout-percent">
<div class="col width-1of3"></div>
<div class="col width-2of3 last"></div>
</div>
And then the fixed width rewrite:
<div class="layout-fixed">
<div class="col-left width-300"></div>
<div class="col-main last"></div>
</div>
Thats a pretty substantial improvement in ease of use.
People Make Mistakes
As simple as this is, someone will get it wrong. They may have it totally wrong and think that the framework is at fault. They may also introduce subtle hard to find bugs. Some ‘clever’ developer will mix the fixed width and percentage width column attributes together in the same container and this could produce unexpected results that might not show up on the particular browser they are testing on. They will probably break IE-6 and not know about it till the customer tests it. To help them avoid this we can detect misuse of the framework and flag the offending element.
We can detect use of the wrong column type within a particular layout type. We can also detect some misplacement of the ‘.last’ attribute and enforce that its always present on the last block. The ‘.col-main’ has to be the last element in the source order and we can use adjacency selectors to check for this too. Unfortunately we can only flag the element that comes after ‘.col-main’ but this should be enough.
The selectors are written individually so that when a developer inspects the error with Firebug or the IE Developer Tools they can see the exact cause of the error. This beats having a huge list of potential errors to pick from. I also used a dashed red border so this error detection does not conflict with the debugging support. Developers should not be adding borders to layout elements because it will break the layout. Because borders add to the width of an element adding them will break any percentage width layout where the element widths add up to 100%. Note: 100% + 1px border > 100%, a fact that programmers tend to forget. In one case I have had to turn on a border and only turn it off when they get it right. This could all be included in a seperate css file and removed in a release build.
This technique has already proven to be useful. I get colleages asking why all they got was this annoying red border and I was able to quickly spot the error. Think of it as a CSS compile time error checker.
/* Layout Error Detection Support (remove in a production environment) */
/* Detect misuse of the col-* and width-XXX classes based on their container */
/* needs !important because it can be turned off on the last column if .last is applied correctly */
.layout-percent > .col-main {border: dashed 8px red !important;}
.layout-percent > .col-left {border: dashed 8px red !important;}
.layout-percent > .col-right {border: dashed 8px red !important;}
.layout-fixed > .col {border: dashed 8px red !important;}
.layout-fixed > .width-1of1 {border: dashed 8px red !important;}
.layout-fixed > .width-1of2 {border: dashed 8px red !important;}
.layout-fixed > .width-1of3 {border: dashed 8px red !important;}
.layout-fixed > .width-2of3 {border: dashed 8px red !important;}
.layout-fixed > .width-1of4 {border: dashed 8px red !important;}
.layout-fixed > .width-3of4 {border: dashed 8px red !important;}
.layout-fixed > .width-1of5 {border: dashed 8px red !important;}
.layout-fixed > .width-2of5 {border: dashed 8px red !important;}
.layout-fixed > .width-3of5 {border: dashed 8px red !important;}
.layout-fixed > .width-4of5 {border: dashed 8px red !important;}
.col-left.last {border: dashed 8px red !important;}
.col-right.last {border: dashed 8px red !important;}
/* Check that no columns come after .col-main */
.col-main + .col-left {border: dashed 8px red !important;}
.col-main + .col-right {border: dashed 8px red !important;}
/* Enforce that the .last attribute is set on the last column (only works in newer browsers!) */
.col:last-child {border: dashed 8px red;}
.col-main:last-child {border: dashed 8px red;}
.col:last-child.last, .col-main:last-child.last {border: none;}
Alternate Proposals
We do have two other competing ideas on how css should be used. I’m presenting them here with examples because it should be clear in either case that the approach is sub optimal. We have backers of either approach in house, either they activly advocate the solution or have rejected all alternate solutions. The point is I have seen these two ideas expressed by actual people in some way. It’s likely that you will face similar ideas when dealing with programmers in your work.
Class == 1 Style Rule
The first is to give the programmer attributes that map 1:1 with style rules:
.clear-both {clear:both;}
.overflow-hidden {overflow:hidden;}
.float-left {float:left;}
.width-300 {width:300px;}
<div class="clear-both overflow-hidden">
<div class="float-left width-300"></div>
<div class="overflow-hidden"></div>
</div>
We do have code like this being written right now. Its being targeted at IE6 and only IE6. Its not clear how this will be evolved in the future to support new browsers. There is nothing semantic about this. There is no way that you can safely change the way ‘float-left’ works in the future to support new browsers. ‘float-left’ does not indicate that a fixed width layout is being employed. You might as well go back to putting everything in the style tag. In the long term all of this code is going to have to be be rewritten.
The IE-6 compatibility style rules are not included. I left them out so you wouldn’t think I was being pathological. It’s not clear how to introduce them into this model. Should ‘.ie6-margin-right-hack’ be a class? If you define ‘.margin-right–3′ does that set a negative margin on all browsers or just in IE6? If you define ‘.overflow-hidden’ should that have the IE hack in its code or not? Let me at least try to give this a fair attempt:
.clear-both {clear:both;}
.float-left {float: left;}
.width-300 {width:300px;}
.overflow-hidden {overflow:hidden;}
.ie6-overflow-fix {_overflow:visible;}
.ie6-zoom-hack {_zoom:1;}
.ie6-width-auto-hack {_width:auto;}
.ie6-margin-fix {_left:-3px; _margin-right: -3px;}
<div class="clear-both overflow-hidden ie6-overflow-fix ie6-zoom-hack">
<div class="float-left width-300 ie6-zoom-hack"></div>
<div class="overflow-hidden ie6-overflow-fix ie6-zoom-hack ie6-width-auto-hack ie6-margin-fix"></div>
</div>
I am deeply uncomfortable with this approach. Its not semantic and we are back to programmers being a CSS experts, which they are not. Reuseability of the individual styles is high but they fail to capture knowledge about cross browser issues and make that re-usable. I think the example above should be sufficiently bad to deter anyone from going down this road.
Class == Result
The other proposal is at the other end of the spectrum. A 1:1 mapping with the desired result. E.g.
.col-left-300 div {overflow: hidden; _overflow:visible; _zoom:1;}
.col-left-300:first-child {float:left; width: 300px; _margin-right:-3px;}
<div class="col-left-300">
<div></div>
<div></div>
</div>
This style does a good job of capturing cross browser bugs and making that knowledge reusable. Its also clearly simple to use. That is the main argument for this style, its ‘simple’.
The problem is its only simple for the programmer when they use the style correctly. It assumes much about how the document is structured. Its not re-usable in any other situation. If you want to define a ‘.col-left-250′ attribute you need to copy two existing style rules and modify them both. That’s pretty much the definition of violating the DRY principal. This is the same approach that YUI takes with their templates. They are difficult to modify if you want a specific pixel width layout. This is one of the reason why I stopped using YUI. The programmer/designer stuck maintaining the CSS has to write gallons of code. When the programmer making a new layout needs something custom they have no idea how to make that happen so they abandon the framework and go back to using the style attributes.
Here again I made the example above very simple. It gets increasingly complex if you want to use more than 2 columns in a layout. There is not enough browser support to make this happen using pseudo-selectors. The :last-child and :nth-child psudo-selectors are in CSS3 but are not supported in any flavor of IE, including IE8. It’s inevitable that more complex layouts will require additional attributes on the columns to mark their indexes. E.g.:
.three-column-150-200-auto first {float:left; width:150px; _margin-right:-3px;}
.three-column-150-200-auto second {float:left; width:200px; _margin-right:-3px;}
.three-column-150-200-auto third {overflow: hidden; _overflow:visible; _zoom:1;}
<div class="three-column-150-200-auto">
<div class="first"></div>
<div class="second"></div>
<div class="third"></div>
</div>
This will lead to a huge CSS file with lots of duplicated code that cant be re-used. Programmers will have to be CSS experts to extend the framework. The long term maintainability of this approach is doubtful. I would say that I prefer this over the first alternative but its still not as optimal as the Attribute CSS approach.
Results
Attached (attribute css.zip) is the re-factored result of all this. It took most of my spare time over the weekend. Its got a test suite based on the OOCSS test pages. All the error detection cases are demonstrated as well as nesting of layout types. The jury is still out on how well this is accepted by the programmers I work with. I think I am on the right track though. Thinking in terms of attributes, providing debugging where appropriate, trying to make names and usage as consistent as possible. The CSS this produces makes sense, promotes css code re-use, captures cross browser bugs and looks maintainable. Nuff respect and thanks goes to Nichole Sullivan for giving me a 90% solution to start with.