Even One Word

The Blog of Nathan St. Pierre

top

CSS Abstraction Thursday, April 22, 2010

This post is being created due to the fact that two things have come up which are new to me.

Point the first

The first is that I've never had a meaningful conversation on twitter that needed to elevate to a better, more spacious medium. In most cases, points brought up in 140 chars are often summed or rebutted in just as few, but I've actually had my first conversation via twitter that really needed to escalate further. My instinct was to hurriedly write this response and put it up there, but I realized it would be a disservice to all people involved if I didn't first do extensive research and experimentation.

Point the second

The second point is actually not fully new to me per se, but it's something that's constantly on my mind. I've used a variety of forms of code abstraction over the years, but the one thing I've never wanted to boil into another level of abstraction was basic markup. I never wanted to abstract away the basic layout of my form, including main container blocks and other general markup. My fear was that this would lead to lazy automation and totally jack up the formatting of my actual markup. For example the following code is dynamically created by using an abstraction method (which generates spacing and IDs dynamically):

Dynamically.
Generated.
Content.
Loses.
Semantic.
Meaning.

Here is the same code preserving its semantic nature by hand:

Personally.
Semantic.

This was true in html 4/xhtml 1 due to the fact that so many more or less generic containers with semantically relevant classes/ids were a necessity, and meaningful markup was especially hard to maintain in an abstract manner. As far as the semantic identities, sure you could have easily done those all by hand in the original generated content, but how does that save time or typing? And the first iteration of the markup is actually syntactically more consistent than the second, but the second uses tabs and newlines in a way that provides more semantic detail. Considering the fact that in (well-written) semantic HTML5, you should really be using all of the appropriate semantic tags. And considering that you can't really loop through or abstract away that information, wouldn't it be smarter to just write that by hand? Let's see a possible example of this code in html5 elements...


  HTML5.
  
Formatted.
Semantic.

As you can see here, the need for classes and IDs is more or less nonexistant, and styling these elements would additionally be simple.

The Necessary Qualifications and Apologies

Keep in mind, I'm only saying this for the large markup elements. I fully agree with the programming concept of DRY (Don't Repeat Yourself), and thus hand-writing a thousand-line table that would loop through and dynamically retrieve all of the data from your database and place it neatly in a tabular markup format would be a waste of time and effort. But for the sake of these large elements, adding an additional layer of abstraction could very well put me beyond my abstraction point, not due to my inability to abstract my understanding that far back, but the added cost of maintenance and conversion could end up being a net loss.

So, I was digging around and stumbled upon a really neat website. Colin Fahrion actually uses a form of CSS abstraction called SASS, which stands for Syntactically Awesome StyleSheets. I looked into it, and (perhaps a bit hastily) concluded that while it was powerful and had the ability to do some interesting things, it was really like most Ruby/rails abstraction tools and just provided a way for other languages to look like Ruby. So, I made an admittedly regrettable bon mots on twitter in which I proposed that "#sass is basically CSS for people who hate brackets." A flurry of tweets flooded my ChromedBird, and for the most part people accused me of not being able to grok the concept of abstraction, specifically CSS abstraction.

I can understand that point, and I realize my original post came off that I seriously thought abstraction was a waste of time. I don't have a clip, but it reminded me very much of an episode of Family Guy...

Peter: Chris, I'm just as serious as when I saw Paul Reiser do stand-up.

(Flashback to Paul Reiser stand-up)

Reiser: So what's the deal with airline food? Is this stuff bad or what?

Peter: Aw, that's not nice; those chefs work really hard.

Reiser: And what's with those Starbucks, huh? They're everywhere.

Peter: Uhh...a lotta people want coffee; that's supply and demand, it's the foundation of our entire economy Paul...

Reiser: And who do I talk to about those long lines at the atm? That's what I wanna know.

Peter Not me, Mr. Reiser. Someone who has time to fritter away, but not me.

One of the first responses I gott was from Chris Eppstein, who linked me to his blog post. In it, he discusses a variety of methods of abstraction, specifically SASS, and another framework that he works on called Compass. When I brought up the fact that CSS has built-in ideas of inheritance and specificity, he linked me to his next blog, CSS Class Inheritance: Abstracting Selectors. To be fair, I was tweeting on ground that had been much more extensively blogged before me, so I came off a little snide, and for that I apologize. So I'll go into further detail here, specifically on the issues that have kept me from using stylesheet abstraction so far.

Variables

SASS:

!article_font_color = #333
.article p
  color= !article_font_color
.widget .article-snippet
  color= !article_font_color

CSS:

.article p {
  color: #333;}
.widget .article-snippet {
  color: #333;}

Analysis

Now to be fair, this might not be a great example, but looking at this, it seems to be a good deal of extra work for the use of a variable. The argument, I'm sure, is that it's easier to maintain if the client decides all instances of the color #333 need to be replaced by #111 (a darker shade of grey), and in this case, it would definitely save you the trouble of a find/replace. But keep in mind that there are other instances in which this would actually create more work for you: specifically the case in which you decide that you don't want all the instances where you used !article_font_color to actually be that color. So now you have to go and change the variable name, and create a new variable to represent that other color, and then call it in the appropriate places.

Again, I'm sure this is arguable, specifically by the fact that it's a question of your existing workflow and changes you'd have to make to your methodologies. There is also the argument that even if it's more work, this becomes more maintainable by a group because !article_font_color is more human readable than #333. But: that's based on the assumption that you already know the business requirements or nomenclature of your existing page setup, which basically means you're trading one learning curve for another. I'd rather look and see what color the element is than see another symbol that contains no color data. !article_font_color means less to me without context than #333 or #111 or even #2da9ee (which is a common shade of blue that I use, so it is quickly recognizable to me).

Conclusion

I think in the event that your design is already fleshed out to the point you have identifiable variable structures (in which !article_font_color would be more than meaningless), and you are working with at least a small team of developers, variables could save you some headaches. However, I'll argue that they trade those headaches for other ones, and in this case would actually frustrate me because I'd have to do the same amount of searching in the file to keep verifying what colors I'm coming across. Especially due to the fact that inspecting these stylesheets in the browser will show that color code, so I'll have to do some backwards references to the source code to find out what those colors are stored as in my variables.

Functions

The next example he gives is the following: SASS

!base = #60A
!highlight = lighten(!base, 33%)
!shadow = darken(!base, 33%)
div.box
  background-color= !base
  border:
    width: 2px
    style: solid
    colors= !highlight !highlight !shadow !shadow

Ruby Definition

# Darken a color by 0-100%
def darken(color, amount)
  hsl = Compass::Colors::HSL.from_color(color)
  hsl.l *= 1.0 - (amount.value / 100.0)
  hsl.to_color
end

CSS Result

div.box {
  background-color: #6600aa;
  border-width: 2px;
  border-style: solid;
  border-color: #a41bff #a41bff #440072 #440072; }

Analysis

So here is something that is a fundamental argument for pretty much any language's abstraction. In this case, you're creating new functions that aren't currently supported by CSS. I can't really argue against the utility of a function in general, although this particular example brings up the point of maintenance again. The function in this case was written pretty well, giving you the option to specify percentages so you can tweak the result. Extracting a hypothetical in this case yields the following issue: the client changes their background color to the same color as this border so it becomes invisible. Naturally, you cogitate that "okay this saves me time because all I have to do is change the base color, and the rest is done for me, huzzah!" The problem in this particular scenario would be that you need to pick a color that was not only a good contrast for the background color, but one that was capable of range so that darkening and coloring would actually provide a visible effect. In the event that they didn't, now you need to go back and alter the function, possibly the amounts of each percentage of those function calls. And again, if you've applied this function in multiple places but now those colors have changed to different values, you have the same amount of work to do, if not more.

Conclusion

Functions are very useful, and to be honest, I think this is the most useful method of abstraction available in SASS (though others would argue that "mixins" are the basic unit of abstraction in SASS). However, --and this is my main argument against CSS abstraction in general-- not all aspects of presentation are quantifiable. Automating and abstracting away things like sizes of elements into the appropriate form is definitely useful. You can calculate a Fibonacci series of elements by ratio without having to hard-code those calculations. But a majority of aesthetic aspects of presentation are so subjective you can't really trust a program to appropriately automate things like readability and contrast ratio. Again, this is a personal preference, and I'm saying this because I've spent 3x as long trying to get algorithms to behave than just trusting my eyes and making adjustments to a cleanly formatted stylesheet.

Mixins

As Chris describes them, mixins are the "contents of the selector without the selector." Here's an (awesome) example: SASS:

// Cross Browser Transparency (Yuck!)
=opacity(!opacity)
  :opacity= !opacity
  :-moz-opacity= !opacity
  :-khtml-opacity= !opacity
  :-ms-filter= "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + round(!opacity*100) + ")"
  :filter= "alpha(opacity=" + round(!opacity*100) + ")"

ul.nav
  li
    +opacity(0.75)
    &:hover
      +opacity(1)

CSS Result:

ul.nav li {
  opacity: 0.75;
  -moz-opacity: 0.75;
  -khtml-opacity: 0.75;
  -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=75);
  filter: alpha(opacity=75); }
  ul.nav li:hover {
    opacity: 1;
    -moz-opacity: 1;
    -khtml-opacity: 1;
    -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
    filter: alpha(opacity=100); }

Analysis

This is arguably the coolest feature of SASS. I think to be honest if there were any reason to switch, it'd be this. Especially given the fact that in our current css3 universe, you need to be able to support a variety of different sets of parameters if you want to use the latest effects (gradients and shadows are the most obvious case). By utilizing this, you actually save yourself a lot of time in general, and this definitely makes things more maintainable.

Conclusion

Again, this is a matter of opinion, but I think the real thing that most of this brings up is that browser-specific logic needs to be contained in browser-specific code. A lot of people would argue that you should use JavaScript to find which browser is being used and style accordingly. A great example of this is Chris Coyier's article on jQuery CSS Abstraction. In his second example he makes the jQuery equivalent of this fix...

//just works
//also alternate CSS syntax
$("#thing").css({
  opacity: 0.5
});

Now what's wrong with this? Obviously, someone will have to point out that if you have scripts turned off, or if your scripts have failed (God forbid that {sarcasm}unlikely event{/sarcasm} ever occurs), your styles are useless. So would some CSS that was generated via abstraction be a better idea? In this case, I think it would. I think this example proves that SASS is worth something, specifically in this evolving world of HTML5 and CSS3.

Grand Conclusion

So what do I think? I repeal my earlier comment, I think that I was pretty well humbled by the community in this case... which happens. This is not just an alternate syntax of CSS, it is an abstracted language, and it provides a lot of ways to speed up your creation and maintenance of pretty well-designed stylesheets.

That being said, however, I will argue (in the spirit of Fred Brooks) that there is No Silver Bullet. In his blog, Chris Eppstein points out that there is definitely a learning curve, and a few other negative issues.

The ones he didn't point out were the ones that I think are really arguments against abstraction in general, and I've mentioned them in this blog but will summarize them now.

  1. Abstraction does not always make your code more maintainable. Now you have to maintain the abstracted code and the generated code. If you need to debug, you need to see why your abstraction is off by viewing the source of your generated code. Now if the browser simply UNDERSTOOD this abstracted code, it would add yet another layer of debugging, when you have to make sure your SASS engine was parsing correctly. So the argument is essentially that standards exist for a reason. Changing the standard is tricky, especially when you use one platform to do it.
  2. Automating visual elements usually requires even more tweaking than stipulating those things individually. I won't argue that there's never a good reason to do it, but I will argue that you can't say that you'll save time in all cases when you attempt to automate multiple visual elements.
  3. Abstraction of markup in general (both html and css, or xslt and xml in other cases) tends to cause additional confusion, as content and presentation are supposed to be as semantic and specific as possible. At the same time, you lose some of the value of implicit styling when you are creating a stylesheet that essentially creates an explicit language out of your selectors, especially when you may have intentionally used non-intuitive methods to achieve specificity.

Do I think abstraction will save the world? No, especially not of markup. But do I think it could help certain people out in certain situations? Absolutely, and I think that if you're a ruby coder, and you develop sites that would need these specific examples of markup, you're hurting yourself by at least not checking this out.

More reading

There are some abstraction methods that I think would be just fine in maintaining implicit structure, namely the Zen Coding format (specifically ZenCSS), which only simplifies the basic elements of the language, doing away with a majority of redundant sigils.

As always, A List Apart beat everyone to the punch and wrote (arguably) a definitive guide on this back in 2007.