CSS Repeats Itself

CSS has a noble goal: separating content from presentation. The sad truth, though, is that the implementation of that goal is unbelievably hideous. We’ve spent so much time over the last decade first evangelizing the goal and then excoriating browser vendors for not correctly implementing the specs, that we’ve neglected to notice some fairly fundamental flaws in the language itself. The first of those is a violation of the DRY principle: Don’t Repeat Yourself.

When writing code of any kind, you want to avoid repetition. Repetition causes problems. When the same information appears in two places, it can get out of sync, and this is a common source of bugs. Sometimes the bug isn’t in the repetition itself. Sometimes it’s an independent bug that appears in more than one place. In this case, it needs to be fixed in more than one place. However, it’s all too common to fix a newly discovered bug in one place and not another.

Traditional programming languages provide numerous mechanisms for avoiding repetition: global and local variables, named constants, methods and functions, classes and class libraries, macros and more. In a language like Java, Ruby, or C, there’s little to no excuse for repeating yourself. Unfortunately in CSS there’s little alternative.

For example, consider the “Holy Grail” of CSS, a three column liquid layout with header and footer. Matthew Levine demonstrates the technique thusly:

body {
  min-width: 550px;      /* 2x LC width + RC width */
}
#container {
  padding-left: 200px;   /* LC width */
  padding-right: 150px;  /* RC width */
}
#container .column {
  position: relative;
  float: left;
}
#center {
  width: 100%;
}
#left {
  width: 200px;          /* LC width */
  right: 200px;          /* LC width */
  margin-left: -100%;
}
#right {
  width: 150px;          /* RC width */
  margin-right: -150px;  /* RC width */
}
#footer {
  clear: both;
}
/*** IE6 Fix ***/
* html #left {
  left: 150px;           /* RC width */
}

Look at all the repetition! The two values in this small fragment, 150px and 200px, are repeated five and four times respectively.* Suppose you need to adjust the size of the right column by a few pixels. You need to change 5 separate lines of code. This is, of course, a simple textbook example. In real world CSS stylesheets, you usually need to change even more code; and the lines you need to change are more widely separated (maybe even in different files) and less likely to be correctly documented.

The lack of variables and macros in CSS more or less requires authors to repeat themselves repeatedly, and then repeat themselves again. This is a bad thing that leads to complex, unmaintainable, incomprehensible code. CSS desperately needs simple variables and macros so it’s possible to do something like this:

#define LC 200px;
#define RC 150px;

body {
  min-width: 2*LC + RC;
}
#container {
  padding-left: LC;
  padding-right: RC;
}
#container .column {
  position: relative;
  float: left;
}
#center {
  width: 100%;
}
#left {
  width: LC;
  right: LC;
  margin-left: -100%;
}
#right {
  width: RC;
  margin-right: -RC;
}
#footer {
  clear: both;
}
/*** IE6 Fix ***/
* html #left {
  left: RC; 
}

Now the values for the right column and left column widths are defined once each, and any change made to them cascades naturally to all uses of those values. You could certainly take issue with the strawman syntax I suggest here, but the need for something like this is undeniable.

I’m not sure why CSS doesn’t have this. I suspect there was a naive belief among the spec authors that they needed to avoid variables and macros because these are “programmer tools” and they were creating a spec for artists and writers.

However, leaving out necessary tools only emasculates a language and makes harder what people need to do. It doesn’t make anything any easier. Sad to say, after ten years of CSS we still don’t have any variables or macros, nor are any likely for the foreseeable future. Perhaps, though, we could define an intermediate language that does offer such support, and which could be compiled to standard CSS. You could even implement it dynamically on the server as we often do with XSLT. That is, upload a CSS+V stylesheet; and let the server compile it to CSS before transmitting it to the browser. Any takers?


*If you counted three and four, look closer and count again. It’s tricky points like this that make repetition so deadly in code.

20 Responses to “CSS Repeats Itself”

  1. Juan Manuel Caicedo Says:

    Hi,

    There is something called ‘Cascading Style Sheets Server-Side Constants’ implemented in PHP:
    http://www.shauninman.com/plete/2005/08/css-ssc-quickie

    This post, and its comments, is also usefull:
    http://meyerweb.com/eric/thoughts/2005/08/31/the-constants-gardener/

  2. Eric Burke Says:

    “upload a CSS+V stylesheet; and let the server compile it to CSS before transmitting it to the browser”

    If you have to rely on a server, then you may as well simply use something like JSP or a templating language such as Velocity or Freemarker.

    Having such features built in to CSS, however, would be nice because you could test offline using nothing more than a text editor and browser.

  3. mike Says:

    Lately, I’ve been hearing a number of complaints about CSS (see http://www.raizlabs.com/blog/2006/09/ten-reasons-why-css-sucks.html) and it makes me glad. CSS was a good start, but we need more. Yeah, there’s the dry thing. Organizing your CSS is also difficult. Hopefully the current unrest will lead somewhere good.

  4. Kevin Greer Says:

    If you code your CSS’s as JSP’s, then you can use Java for constants and scripting. All you have to do is configure your web server to treat the “.css” extension the same as the “.jsp” extension.

  5. Laszlo Marai Says:

    Yes, you can use any text macro language to generate your CSS just as your HTML. But you don’t even need the server side for this. Velocity (and possibly the others like Freemarker, Webmacro) isn’t tied to a servlet container. You can use it to generate any text by running a simple command then you can upload your static CSS to the server.

  6. Kevin Greer Says:

    If you don’t mind preprocessing your CSS’s offline then you can also use cpp (the C preprocessor) or m4.

  7. Taylor Gautier Says:

    I still don’t get how that three column layout is supposed to be better than tables. All of the hardcoded px values really irk me, and the fact that CSS “columns” still can’t be done right after all these years proves something is horribly wrong.

    Look at the lowly table, introduced sometime in the late nineties, it does three column layout standing on its head:

    <table>
    <tr>
    <td>column1</td>
    <td>column2</td>
    <td>column3</td>

    </tr>
    </table>

    The CSS purists in the room somehow think that kind of simplicity is wrong, and as you have so nicely pointed out, it basically STILL impossible to do that simple thing correctly in CSS.

  8. Elliotte Rusty Harold Says:

    The problem with the table version is that the source order must match the display order. This is bad for screen readers because they have to read the entire left sidebar before reading the content in the middle.

    Possibly there were simpler solutions to this problem than CSS. For instance, readorder attributes could have been added to table cells to assist screen readers, or the CSS layout model could have built on top of nested tables instead of the confusing box model. Each CSS stylesheet (not the HTML but the stylesheet) would define a table for the page, and then each element would be placed in one or another of the table’s cells. Perhaps what we need is TSL: Table Stylesheet Language.

  9. Lars Borup Jensen Says:

    Its funny how you focus making CSS better but overlooking what really strikes me as being HACKS!

    #container {
      padding-left: LC;
      padding-right: RC;
    }
    
    #right {
      width: 150px;          /* RC width */
      margin-right: -150px;  /* RC width */
    }

    Whats wrong the above CSS. First of – you have to using padding for overall layout and margin-right: -150px whats up with that.
    It should have been:

    #container {
        width: 900px;
        position: relative;
    }
    
    #left {
        width: 150px;
        position: absolute;
        left: 0px;
        top: 0px;
    }
    
    #right {
        width: 150px;
        position: absolute;
        left: #container.width - width;
        top: 0px;
    }
  10. Elliotte Rusty Harold Says:

    No, that doesn’t work. It sets a fixed size for the total layout, one that’s quite a bit too large for some environments and users and too small for others. The main content column needs to be liquid. It has to adjust to fit the available space.

  11. Andrew Assarattanakul Says:

    Well if people are picky about pixels being specified, this will work if you don’t want to have any borders or margins for the containers.

    #container{
    	width:90%;
    	margin:0 auto;
    }
    #left{
    	width:25%;
    	float:left;
    }
    #right{
    	width:25%;
    	float:right;
    }
    #center{
    	width:50%;
    	float:left;
    }
  12. Dan Kubb Says:

    There’s one other thing I always wished that CSS allowed: nested scoping. Here’s an example that better illustrates the idea (this was intended to be indented, not sure how it’ll display):

    form {
      fieldset {
        input {
          [type=text] {
            /* only applies to elements matching the CSS rule "form fieldset input[type=text]" */
          }
          [type=button {
            /* only applies to elements matching the CSS rule "form fieldset input[type=button]" */
          }
        }
      }
      div {
        input[type=submit] {
          /* only applies to elements matching the CSS rule "form div input[type=submit]" */
        }
      }
    }

    With most of my stylesheets I arrange all the form related rules separately from the table related rules. If I could nest them like the example above, I could cut out a lot of duplication as well as keep related rules together more easily.

  13. Streamlining CSS | Phil Wallach Says:

    […] I had been thinking about this for a while, and was considering coming up with some sort of CSS generation language.  Then, as these things happen, I read CSS Repeats Itself and everything became clear. […]

  14. zappini Says:

    I tried to study and use constraint systems, including various layout managers, for a while. I gave up.

    I now support using special purpose layout managers. My DesignGridLayout, inspired by the work of Mullet and Sano, is my first attempt using this strategy. I envision additional layout managers derived from use cases and platform look and feels. Stuff like login boxes, property sheet editors, etc.

    If I cared more about the web, I’d probably completely bypass CSS layouts and implement a layout manager in JavaScript.

  15. gman Says:

    It’s funny to me that people complain about tables because the content is not separated from the layout and yet CSS comes no where close to separating content from layout. If content was truly separate from layout I should be able to specify the content in any order. I should also not have to nest images in 2-3 layers of divs just to get a dropshadow. I should not be resigned to displaying articles in the order they appear in the html.

    And, as far as Taylor’s example table above, CSS still can’t handle with the content is larger than the defined space. Tables will adjust themselves to fit the content. CSS still can’t 🙁

    http://greggman.com/edit/editheadlines/2004-06-15.htm

  16. johnk Says:

    I do this all the time:

    .selector { property: }

    It’s a little ugly, but it works, and is pretty clear to me.

    If you want to be more snazzy, you could do something like those server side constants, but, maybe a little less intrusively. You could write a preprocessor that will magically replace values in the CSS that’s formatted with the macro names after the values, like this:

    #right {
    width: 150px; /* rightWidth */
    margin-right: -150px; /* rightMargin */
    }

    The preprocessor would just look for comments after values, and replace the value. If you wanted to be really clever (and who doesn’t) set the value of the macro by the first usage of it. That is, the first occurence of “150px; /* rightWidth */” would define rightWidth as “150px”. Maybe a text editor hack could be used to “process the macros” and allow you to do it all client-side.

  17. David Singleton Says:

    I completely agree with the gist of your post.

    CSS is far from perfect. Ignoring browser bugs there are still many things that make my life difficult on a day to day basis at work. Personally I would find a CSS variable system far more useful for colours than for px widths.

    Some kind of CSS variable system would be a huge step forward, and many people have been using the ‘CSS+V’ solution for years, in itself this shows there really is call for it to be discussed and included in the CSS spec in the distant future.

    However I disagree with the example you’ve chosen, and most of the comments that people have been left.

    There seems to be quite a divide between traditional programmers and those who work with CSS/HTML. I work with both client side and server side web coding on a daily basis, so I’d like to think I have a slightly balanced opinion (I’m sure many might disagree)

    The example you chose is a particular method of doing a traditional 3 col layout that happens to rely on a fair amount of negative margins and position juggling. It’s a quite extreme example to take. There are other methods that require far less repitition to achieve the same goal.

    While I’ll willingly admit that CSS has its flaws and needs much improvement, I’m really quite baffled as to how people still prefer tables. The only real advantage they offer is the ease of an equal height multi-column layout, something which is achievable with CSS, in some cases using a combination of a single table to layout columns and then CSS for everything else is by far the simplist solution.

    It seems that a lot of the comments here advocate tables rather than CSS entirely, not just for layout. Once you get beyond simple columns, tables become incredibly complex compared to CSS. I’ve spent a lot of time working with both table and semantic/CSS sites, and by far semantic HTML/CSS is _much_ easier to build and maintain. I’d love to hear someone who has to work with the resulting HTML for any length of time argue the pluses of tables.

    Phew, bit of a rant.

  18. johnk Says:

    A complete solution for liquid layouts, that doesn’t involve programming, probably doesn’t exist. Look at how complex TeX is… and that’s specially tuned to produce text flows for a pretty narrow range of documents (basically, textbooks and math papers). Web pages can be books, blogs, hypertexts, newspapers, and brochures.

  19. Andy Stewart Says:

    If you’re using Ruby on Rails, you may be interested in css_dryer, a plugin I recently wrote to avoid duplication in selectors and introduce variables.

  20. Usabilità « Usability, accessibility and the Web World Says:

    […] Ecco qui il link al post: CSS repeats itself. […]