We just ported our tree from straight CSS to Compass and without doing anything clever at all, it's been a huge win.
Straight off the bat, going from CSS/Blueprint to Sass/Compass got rid of ~300 lines of styling, simply because Sass is so much more expressive.
Then we went through the code and extracted all the color literals and stuck them in a variables file. Instantly we were able to play with site colors without search/replace drama. The stylesheets themselves also got far more readable, since you never have to work out in your head what a particular hex string probably meant. Little things like that remove friction from the dev process, and are totally worth it.
Of course, once we had all the colors in one place, it became obvious that we had, for instance, 4-5 different oranges all within 15% of each other. We kept the variable definitions, but made them all the same base color.
We repeated the same process with font sizes.
Finally, we went through all our markup and evicted the Blueprintisms, moving them to the stylesheets via Chris' Blueprint Compass mixins. This was more than a "semantic vs. visual" hygiene win. Playing with column widths involved finding where Blueprint classes were passed in or coded into markup. Now they're right there in the CSS, where you expect them to be.
We've got tons of opportunities to reduce the stylesheets further by macro-izing or mixin-izing, but just by transliterating our code from CSS to Compass we got a big enough win to be happy for this dev cycle.
Can't recommend Compass highly enough. Great work, Chris.
Wow, that's awesome. Many people have it in their mind that sass and compass are more of a "new project" or "rewrite" kind of thing. Thanks for sharing your success with an existing project as a form of refactoring. That sounds like exactly the right way to approach it.
That is pretty awesome - I've been thinking about doing this for a project I'm working on too. Can you give a rough idea of how long it took you to complete the conversion?
I think it's a good thing to have these abstractions available, and as the article notes it's better to keep these at a high level. It's not a call to extend the CSS specification or to add support for abstractions in browsers.
I have always found it easier to avoid bugs when parsers are dealing with canonical forms. In other words, make the browser support the absolute minimum syntax required to achieve every effect in CSS. Anything that's merely "syntactic sugar" should be handled at higher levels, and then "compiled down" into raw CSS.
This restriction not only makes it easier for browsers to agree, but it means that you're only likely to find quirks in your higher-level tools: which, importantly, are outside the end user's domain. Bad tools can be replaced, or you can adjust your flow to insert post-processing steps that fix the output. You don't need as many "debugging tools" because you can simply examine the generated files to tell if something was interpreted correctly (whereas, if a browser isn't seeing things the way you think, good luck figuring that out without extra tools).
Absolutely. Sass is a CSS generator and while it might spark ideas for how a new in-browser stylesheet syntax would work, I don't think that it ever will be that syntax for one crucial reason: an in-browser syntax has full knowledge of the DOM to which it is being applied -- and that really changes how you think about things.
While I think stylesheet abstraction is a good idea, most programmers (including the author) don't seem to use all the abstraction mechanisms that are already available in CSS. For example, the change-all-the-colors-on-the-page-at-once problem would go away if webmasters factored all the elements of a given color into a single rule:
You're also supposed to rely on the cascade for fonts and stuff: set them once on the body and then let all descendants inherit them.
There're some real problems when you want to set, say, the border color of one element to the color of a bunch of others, but a lot of the CSS maintenance problems I see (and have written ;-)) come from developers just going through the list of elements and writing the properties for each. The cascade was supposed to save you from this; CSS already has a pretty powerful abstraction mechanism.
That code is not more maintainable. You've centralized a single color but now you've duplicated a selector.
The single-rule output mode of Sass is an output optimization mode we plan to add to sass for runtime performance. You should write code the way you think about things and let your tools do the heavy lifting for you.
Styles change more often than selector names. If you're using semantic selectors, they shouldn't change much at all.
This is a problem with any sort of abstraction. When you write a function in normal code, you duplicate the function name and call signature in exchange for centralizing the body. In Sass, you're presumably duplicating the variable name in exchange for centralizing its definition. The same goes here, except s/variable name/selector/.
So now when I want to find out where in the CSS file I need to change the font size of an h3, I have to search through all 127 places where h3 is referenced. All you did was point the problem in a different direction.
Or you search for the 3-4ish places where font-size: is mentioned.
Most websites have far fewer different text stylings than they have element types. I'm arguing that this is a better direction to point the problem.
(I'm also not arguing that abstraction frameworks like Sass are a bad idea, but that you should understand how CSS works before you reach for a framework. I suspect that it never occurred to a lot of folks that they should have long lists of selectors and short lists of properties instead of the reverse, because that's not the syntactic cue they get from the language.)
If you don't "get sass", please take a couple minutes to read the article -- if you've never had to live the life of a front-end engineer, you have no idea how hard it is to do that job with the tool that is CSS. Balancing browser support, design, and constantly changing requirements is hard. Doing it without a form of abstraction at your disposal is lunacy.
I would also like to point out that this post is not an advertisement for compass or even Sass. It's a rebuttal to the people who think any advancement in stylesheet technology is unnecessary. Sass is an implementation of these ideas and compass shows their power in action. But it really is an argument for a new approach.
he misses out 2 of the major disadvantages,
1. you add a dependancy / stage of complexity in the build stage,
2. you lose a direct mapping from your dom elements to your css, ie I can click inspect something in firebug and see on what line of what css file I need to change
and to be honest since I dont find css that hard to manage those tradeoffs are enough to put me off using one.
also worth mentioning css variables have been introduced, just waiting for ie? now
These are good points but they are a downside to the approach of code generation, not to abstraction itself. If Sass was interpreted by the browser it wouldn't be the case, so while it's true for the world we live in today, it's not a fundamental issue with the approach itself of having the power of abstraction.
So while I accept these as criticism of Sass, I do not accept them as downsides of abstraction.
BTW, Sass will emit comments to your CSS to help you track down the original source of your styles.
Nevertheless, I think I will add them to the list of downsides, because I don't think we'll have wide browser support for "Abstract Style Sheets" any time soon.
It it certainly possible that by getting fancy with Sass you could lose track of your selectors and styles, but having spent the last several days debugging (Compass-unrelated) style issues with our app, it seems unlikely.
Here's the deal:
* Raw CSS looks exactly like the raw CSS you always had.
* Sass CSS looks like the raw CSS, but is 1000x more readable.
So, the only problem I've noticed is the throbbing pain behind my left eyeball that I get when I have to look at "real" CSS anymore.
(Yes, I do have to search for selector names in my CSS instead of literally jumping to line numbers, but my workflow was never very line-number-centric to begin with.)
From my own experience if you understand the cascade principle and specificity well you can get away with very compact and well written CSS files. Multi-classing is another area where developers are only now leverage to its maximum, for example if you have a look at the way the jQueryUI CSS works, by for example specifying the items you wish at the end of the file as additional classes you can introduce quite a bit of additional functionality in a much simpler way than calling functions.
For example:
.box{define only properties that you
do not want to
change from box to box
}
.boxColors{background:#fff;
color: #f60}
.arial13{font-family:arial;font-size:13px
<div class="box boxColors arial13"></div>
This way your classes can be used as 'functions'. As a rule, define typography and colors and opacity this way and you can keep it simple.
CSS was developed as an abstraction to HTML. Any abstraction to the CSS other than adding pre-processors would create serious integration problems with the DOM model and JavaScript. (See a good discussion of this at http://people.opera.com/howcome/2006/phd/#ch-problems by
Håkon Wium Lie the original developer of CSS).
Any productivity pre-processors our our business and not of the spec developers.
I hate CSS that uses class names as stand-ins for typographic properties. You might as well just use font tags, because that's what you've reinvented.
Instead, I'd rather have CSS classes each represent a semantic attribute of the element being styled. So instead of <div class = "box boxColors arial13">, you might have <div class=tabbedPanel>. In your stylesheet, you style tabbedPanel with the appropriate background, border, font, etc.
Also don't neglect the value of containment selectors - if you want to style all tabbed panels a certain way, but only if they're in the main results, you can do:
I am well aware that you should try and minimize classes in the HTML.
However, on large sites where, multiple people might be publishing pages with slight variations, it is preferable NOT to touch your CSS too often. The multiple class method gives you some means to control it. It also adds tremendous flexibility for libraries (see jQuery UI's method of handling multiple classes for styling widgets).
Naming your class="tabbedPanel" has added no semantics to your contents and there is no such thing as 'a semantic attribute' defined nowhere. It is just a good naming convention that helps the developer and the designer. Simply there is no semantic meaning to a <div> it is just a container.
How would you handle two tabbed panels, one that is red and one that is blue?
> How would you handle two tabbed panels, one that is red and one that is blue?
Why is one red and the other blue? Surely there's some functional difference between them? I'd rather call them <div id=hot class=tabbedPanel> and <div id=cold class=tabbedPanel> if that's what they're really representing.
I have nothing against multiple classes - I've written a fairly-well-used JQuery UI Combobox plugin, and I follow the normal JQuery UI conventions for class names there (ui-combobox, ui-combobox-item, etc.) I do have something against replicating typographic conventions as class names - note that it's class=ui-combobox-item, not class="displayblock height1em border1pxsolidblue fontweightbold".
<div class="box hot"> and <div class="box cold">, hot is selected and cold it has been dropped in a 'recycle' container.
I also try to minimize id's, for example I could have <li class="cold"> as well.
I do agree with you that one should not just map all the CSS attributes into classes. Perhaps my example was not a good one. However, three or four additional sets of classes, has increased my own productivity, especially during development. I keep a partial CSS file with 5, 6 generic sets. Most large newspaper sites use this method. It all depends in what one needs to do. In this method the style sheet is 'more abstracted', it does not know about a box.cold. If you skinning a wordpress template, you better off to offer the box.cold in the stylesheet, but then you must provide a second style sheet to offer the box.hot! This method also allows a bit of presentational flexibility for new page designs, without playing with the main CSS files or adding another CSS file. Somewhere, along the line there is a golden line I guess.
Consider though that this approach, using classes as functions, violates the primary intent of CSS, separating presentation from markup. You've now gone back to modifying your markup to change the presentation.
I build skinnable sites, where there are multiple different looks for identical markup. The approach above would fail horribly at doing this. Markup should be semantic, a div with a class header or footer is semantically identical to a header or footer tag.
Markup is also quite often tightly coupled to code and much much harder to change than CSS, so this approach fails there as well. Blueprint takes this approach, and I think it's just wrong, this is not how CSS should be used.
What would be really neat would be a syntax to be able to have CSS classes inherit from other CSS classes like mixins so that you could build that header style by mixing in several selectors. Then you could use the above approach without corrupting the markup with style information keeping the look isolated to the CSS file. I see SASS provides, that alone makes it worth looking into.
LessCSS also does this. However, LessCSS lacks the capability to do macro expansion which greatly limits its usefulness as framework implementation language.
Dude, thank you, less is exactly what I've been looking for. It uses existing syntax and adds exactly what is necessary, macros are not. This I can teach to my designers, macros would only confuse them as would a different syntax.
The cascade is a good tool. it's the reason why elements look "right" by default when nested another element within the document.
The application of multiple classes to an element has nothing to do with the "Cascade". Your classes should describe their meaning, not their appearance. CSS was created to abstract away the appearance. Sass makes CSS more "CSSy" in that respect.
In the end, built-in browser support for a higher-order stylesheet syntax will be driven by increasingly complex cross-browser issues, over-the-wire transfer of complex stylesheets and rendering-time performance issues.
It could be the case that a few simple extensions to CSS would enable css preprocessors to do their job better, I have some ideas about that and will post them eventually.
Indeed it can. My bar is a little higher than "decent" though. The design of your site is arguably the most important piece since it's the only part that your users see. Having a domain specific language like Sass or LessCSS makes good sense -- you want to make your front-end engineers as productive as you can.
I hope not. If it did, the "abstractions" would ossify and we'd have to live with their quirks and bugs for the next 10 years. I'd rather the standards people work on defining a useful, reliable, flexible lowest-common-denominator syntax, and the framework people work on making sure we never have to use it directly.
As one of those framework people I can tell you that CSS as it stands is not up to that job yet. Some very basic features are needed on the CSS side, but I'm going to reserve my thoughts on that for a future blog post when I've thought about more thoroughly.
Straight off the bat, going from CSS/Blueprint to Sass/Compass got rid of ~300 lines of styling, simply because Sass is so much more expressive.
Then we went through the code and extracted all the color literals and stuck them in a variables file. Instantly we were able to play with site colors without search/replace drama. The stylesheets themselves also got far more readable, since you never have to work out in your head what a particular hex string probably meant. Little things like that remove friction from the dev process, and are totally worth it.
Of course, once we had all the colors in one place, it became obvious that we had, for instance, 4-5 different oranges all within 15% of each other. We kept the variable definitions, but made them all the same base color.
We repeated the same process with font sizes.
Finally, we went through all our markup and evicted the Blueprintisms, moving them to the stylesheets via Chris' Blueprint Compass mixins. This was more than a "semantic vs. visual" hygiene win. Playing with column widths involved finding where Blueprint classes were passed in or coded into markup. Now they're right there in the CSS, where you expect them to be.
We've got tons of opportunities to reduce the stylesheets further by macro-izing or mixin-izing, but just by transliterating our code from CSS to Compass we got a big enough win to be happy for this dev cycle.
Can't recommend Compass highly enough. Great work, Chris.