Stop Forking Around with CSS3
Issue № 308

Stop Forking with CSS3

It seems like virtually every day there’s a fantastic new example of something amazing you can do with CSS3. Whether it’s as complex as full-blown animations or as (relatively) simple as RGBa colors, drop shadows, or rounded corners, I marvel at how far we’ve come since the lowly days of CSS1 or (shudder) the @font element.

Article Continues Below

The current state of CSS3 reminds me of a typical cinematic device: The scene opens with us, this happy-go-lucky family of developers out for a picnic by the lake on a beautiful summer afternoon. And as we laugh, play, and scamper about entertaining ourselves, the camera pans around, bringing us over to the lake where, beneath the surface, something stirs. Something ominous and foreboding.

Okay, perhaps that was a bit over-dramatic, but what I’m trying to say is that we’re ignoring the beasts within our code, hiding just out of view: forks.

What?#section2

If you’ve been working on the web for more than a decade (I’m probably dating myself), you may remember that bleak time in web design history when JavaScript was a dark art. It earned that reputation because in order to do anything with even the teensiest bit of cross-browser consistency, you had to fork your code.

It was standard practice to have one set of JavaScript functions for Netscape and another set for Internet Explorer. Or, if you were a real masochist, you kept the code combined and created a fork inside the function:

function doSomething(){
  if (document.getElementById)
  {
    // web standards FTW!
  }
    else if  (document.all) 
  {
    // do something in IE
  }
    else if (document.layers)
  {
    // do something in Netscape 4.x
  }
}

In many cases, especially where animation or event handlers were concerned, the code got really gnarly, easily topping even the most atrocious spaghetti code in HTML. The thing about forking your code is that, when you least expect it, it will find a way to fork you right back.

Now, thanks to Web Standards Project browser-standards advocacy and diligent JavaScript library authors, the world of JavaScript is a much nicer place to work and play. Our code is now relatively fork-free, with fewer nooks and crannies in which bugs can hide. Unfortunately, in our rush to use some of the features available in CSS3, we’ve fallen off the wagon.

I think @border-radius was the proverbial “candy” that the forks used to lead us back into their clutches. We all wanted them; hell, we’d come up with myriad ways to fake rounded corners to realize our design dreams. So when Firefox and Safari dangled them in front of our drooling faces, asking only that we add two small forks to our code for the privilege, we jumped at the opportunity.

.this-is-awesome {
  border-radius: 5px;
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
}

They were only two small forks, after all, how bad could they be?

Well, once we had symmetrical rounded corners, we wanted asymmetrical rounded corners. After all, we need to be able to express ourselves and there comes a time when every man, woman, and child must stand up and set the radius of each corner independently! Unfortunately, Webkit doesn’t like the @border-radius shorthand, but that just means we need to be a little more verbose:

.this-is-slightly-less-awesome {
  border-radius: 10px 5px;
  -moz-border-radius: 10px 5px;
  -webkit-border-top-left-radius: 10px;
  -webkit-border-top-right-radius: 5px;
  -webkit-border-bottom-right-radius: 10px;
  -webkit-border-bottom-left-radius: 5px;
}

Oh, and we probably want to make sure Opera and Konquerer can get the rounded corners too (assuming Opera’s supporting them this week).

.this-is-absurd {
  border-radius: 10px 5px;
  -moz-border-radius: 10px 5px;
  -webkit-border-top-left-radius: 10px;
  -webkit-border-top-right-radius: 5px;
  -webkit-border-bottom-right-radius: 10px;
  -webkit-border-bottom-left-radius: 5px;
  -o-border-radius: 10px 5px;
  -khtml-border-top-left-radius: 10px;
  -khtml-border-top-right-radius: 5px;
  -khtml-border-bottom-right-radius: 10px;
  -khtml-border-bottom-left-radius: 5px;
}

Now, are you seeing a problem here? Does it remind you of anything we used to do in our CSS?

.can-you-hear-me-now {
  padding: 10px;
  width: 200px;
  width: 180px;
  height: 200px;
  height: 180px;
}

Or perhaps you were more fond of something in a nice Tan.

.hooray-for-repetition {
  padding: 10px;
  width: 200px;
  height: 200px;
}
* html .hooray-for-repetition {
  width: 180px;
  height: 180px;
}

Call it forking, call it hacking, call it what you will; we shouldn’t be doing it.

Another way#section3

As you can probably tell, forks get me a little riled up. Rather than just getting angry about it, however, I decided to build a JavaScript library that supports clean living (or at least clean CSS): eCSStender. (Actually, the original impetus behind the library was a little different, but we’ll get to that in a bit.)

Boiled down to its essence, eCSStender (pronounced “extender”) is a JavaScript library (akin to jQuery or Prototype) specifically built for working with CSS. On its own, eCSStender doesn’t do anything but analyze your stylesheets. When powering “extensions,” however, eCSStender allows you to use properties such as @border-radius and selectors like @.seven:nth-child(even) without having to resort to forks or hacks.

If you’re developmentally inclined, you’ll be interested to know that eCSStender not only gives you introspection into the style rules contained in your CSS file(s) and provides a framework for testing a browser’s support (or lack thereof) for things like selectors or properties, it also provides a mechanism for you to patch browser support for CSS.

If, however, you’re more inclined toward the design side of things, you’ll be happy to know that using eCSStender and a handful of extensions allows you to write advanced CSS simply. It Just Worksâ„¢.

.this-is-so-much-better {
  border-radius: 5px;
}

Using eCSStender#section4

Using eCSStender is pretty straightforward. You simply include the eCSStender library and whichever extensions you want to run against your CSS.

<script type="text/javascript" src="eCSStender.js"></script>
<script type="text/javascript" src="eCSStender.CSS3-backgrounds-and
-borders.js"></script>
<script type="text/javascript" src="eCSStender.CSS3-selectors.js">
</script>
<script type="text/javascript" src="eCSStender.CSS3-color.js"></script>

Or, you can pick and choose the pieces you want from the currently available extension bundles and create your own custom extension library.

<script type="text/javascript" src="eCSStender.js"></script>
<script type="text/javascript" src="my-extensions.js"></script>

So where do you get these extensions? Well, I have developed a number of extensions that offer CSS3 functionality and have bundled them such that they correspond to the various modules of CSS3, such as borders and backgrounds, selectors, and color. I am also aware of several other JavaScript wizards who are working on other pieces of the CSS3 puzzle, including a multi-column layout and more. A complete list of known extensions is being maintained on the eCSStender website and, if you’re interested, the website also has extensive documentation on how to roll your own.

Once you have included the JavaScript files, you simply go about using the properties as the spec defines them, and the extensions work their magic to translate what you wrote into something browsers understand. The beauty of this setup is that your stylesheets remain clean and fork-free; the extensions do all the dirty work for you.

Extensions are built to be smart, too. Using the eCSStender API, they can test to see whether or not the property or selector you want is supported in the current browser. If the property is supported, the extension doesn’t run, but if it isn’t supported, the extension is activated. So in the case of border-radius, Safari 5 implements the standard property and the border-radius extension is not run, but in Safari 4, which only understands -webkit-border-radius, the extension needs to run and makes the necessary translations. Once the majority of browsers accessing your site are savvy enough to grasp standards without the hand-holding, you simply delete the extension just as you would a conditionally-commented stylesheet for a version of IE you no longer need to support. Easy-peasy.

You’d probably like an example, right? Well, the eCSStender site is certainly one, but I’ve thrown together another one on the off chance you’d like to see a combination of CSS3 goodies in action. It makes use of:

  • @border-radius,
  • @box-shadow,
  • RGBa backgrounds,
  • @transform: rotate(), and
  • @transition (assuming your browser supports it in some form).

Make sure you hover the box.

Yeah, but “extend”-er?#section5

As I mentioned earlier, eCSStender was not originally designed to act as browser spackle; in fact, the original intent for the library, when I began writing it nearly three years ago, was to make it easy for each of us to get involved in crafting future versions of CSS. Through eCSStender, we can play with new ideas and see our concepts realized in an actual browser. It provides an easy way for us to experiment with syntax and see what works and what doesn’t without having to wait for native browser support.

If you’ll indulge me for a moment, I’ll give you an example. Say, for instance, I thought it might be interesting to imbue elements with physical characteristics and I wanted to use CSS to do it. I might create a new property (with a vendor-specific extension of “easy,” to indicate my company) called @-easy-physics-fill that would take one of several substances, such as “lead,” or “helium,” as its value. With eCSStender I can easily build an extension to look for that new property and apply the requisite physical properties. It’s a contrived example, but I think you get the point.

Used in this way, eCSStender empowers us to show the W3C what we want to see next in CSS. And it can help the CSS Working Group evaluate syntax and experiment with new ideas without having to wait for a browser vendor to implement them. This could streamline the process, as ideas that don’t work well in practical application can be jettisoned before the underlying codebase of any browser has been modified. And who knows, maybe the W3C would even consider an extension to be a valid implementation used as part of the standards-development process.

Pitching the forks#section6

As a library, eCSStender has a few modest goals. First and foremost, it aims to offer robust tools for designers and developers to make working with CSS better.

On one hand, it seeks to equalize support for CSS across all browsers by offering a simple set of developer tools that makes crafting extensions easy and implementing them a breeze. On the other, it provides those same tools to designers and developers who are interested in conceiving and then building real implementations of the properties, selectors, and mechanisms they want to see in future versions of CSS.

Perhaps my dream of a world with equalized CSS support across browsers is pie-in-the-sky thinking, but I like pie. Even if it is best eaten with a fork.

61 Reader Comments

  1. Aaron, thanks for the explanations in comment 32. I can see that eCSStender could be overkill in some situations, while being very useful in others. It’s good to consider it on a case-by-case basis.

  2. Regarding vendor prefixes, the following has been echoed many times:

    bq. when browsers update themselves and become CSS3 and W3 compliant, you can easily get rid of them

    That will be true at some point, but depending on how far back you may need to support certain browser versions, you may have those prefixes in your stylesheets for some time. Just sayin’.

  3. So to clean up our CSS we use JS instead of vendor-specific properties? I don’t get it. It’s a waste of time IMO.

  4. I love the idea of ecsstender but I can’t get it to work in IE – specifically rounded corners or text-shadow. Works OK in webkit and mozilla. Should it work in IE or is that still under development?

  5. Mostly web sides users: approx 70% with IE <= 8. Even IE8 isn't using CSS3. I'm testing some of my domains every few days and thing what's happened with IE9? Or shell we get a other Firefox or Chrome or Safari ...

  6. What you’ve assembled is great. It does make for clean use of CSS3. However, I’m taking exception to the premise that you present it in — specifically losing the need to use conditional forks.

    You are still forking, but you are using JS routines to sniff and detect browsers. A practice that you derided.

    And what happens when JS is disabled? Yes, I know. I know. In this day and age, darn near every site on the web breaks horribly with JS disabled, but the fact is that people do still do it.

    Have you considered doing what you do with JS, but doing it with a server-side language? Having it server-side would circumvent the client-side issue.

    I’m not diminishing what you’ve done. I think it’s great. It certainly makes life easier, through use of a standard libraries that degrade gracefully. What I’m taking issue with is the context of “no more forking” that you present it in.

  7. It won’t really be useful for us designers until we can guarantee that it will work on all browsers, our clients expect the site to look the same across all platforms and hence we cannot see this as a viable solution.

    A shame because I would really love to use this, and it would really make our lives allot easier. 🙂

    btw: you probably already know this but there are still issues with FF on your scripts homepage. 🙂

  8. I agree with comment #56… it’s a great idea, but to me the beauty of CSS3 is that we can do a great deal of what used to be done with Javascript instead with CSS3. I can’t bring myself to use Javascript to cleanup my slightly messy CSS3, knowing that if someone were to visit my page without Javascript (think poor mobile phone support, people switching it off for security, etc.), they would not be able to see the shadows, animations, etc. that they might (depending on their browser) be able to see simply with pure CSS.

    I’d also rather not keep patching up old browsers with Javascript… the more we do this (as developers), the more we’re doing to keep those browsers around. If the web was entirely broken in old versions of IE, would anyone continue to use it? And if they did, would they really care about how sites looked, given that so many would have rendering issues?

    The idea (mentioned in prior comments in this thread) of a CSS pre-processor is a nice one though, it’d be great if something like this could be executed in that fashion (a bit like Sprockets?)

  9. i’ve been super excited to try this, but im looking at the Spoon demo site and it’s not working correctly in anything i view it in:

    -Chrome 6: animates and has rounded corners but no shadow
    -Safari 4: rounded corners with shadows, but no animation
    -FFox 3.6: no corners, no animation, no shadows
    -IE8: no containing box at all, two small quote marks

  10. Well this still doesn’t work in FF. I’m on 3.6.12 / Linux and it’s a no go. Works great in Chrome/Linux though. I’m wondering… with this being such a large file compared to just a few extra lines in our CSS… Is it worth it? I’ve always thought that one of the main points of coding semantically was to reduce bandwidth. So, doesn’t this increase [it] greatly compared to a few extra (ugly) lines of code in our CSS? Am I missing something. Seems like a great tool but just not too sure about the size of the file?

  11. I like this idea a lot and found a similar attempt by Lea Verou. But i have similar concerns.
    1. Performance.
    2. We are watching evolution of support over browsers like linear-gradient in chrome 10 How is that taken care?

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA

I am a creative.

A List Apart founder and web design OG Zeldman ponders the moments of inspiration, the hours of plodding, and the ultimate mystery at the heart of a creative career.
Career