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.
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?
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
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
Using eCSStender is pretty straightforward. You simply include the eCSStender library and whichever extensions you want to run against your CSS.
Or, you can pick and choose the pieces you want from the currently available extension bundles and create your own custom extension library.
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?
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
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.
from A List Apart