Last week, I made "Tron" theme for my site using pure CSS. I was pleased with the resulting look, but wasn't pleased with the CSS code: there way way too much duplication. I'd been itching to try out some of the latest tools for generating CSS, so I went back and simplified the code with Sass, using its new "Sassy CSS" syntax, and couldn't be happier with the results. Here's what I did and how Sass helped me do it.
- Original CSS: old_main.css
- Sassy CSS: main.scss
- Generated CSS: main.css
- Live Example of the Sassy Overhaul
Transitioning
Sass now supports "sassy CSS," which is a superset of CSS, just like LESS. Previously, Sass supported only terser, more concise syntax. The older syntax is still supported but not recommended, but IMHO, the "superset of CSS" syntax is Right and the "alternative to CSS" syntax is Wrong. Two reasons:
- It's much easier to get started; just copy your legacy CSS and you already have valid, if ugly, Sassy CSS that you can clean up as you go.
- It's way easier to learn, and there's nothing to "unlearn." You can just start learning new and useful syntaxes right away.
I literally was able to follow the 3 step process shown at the top of the SASS website, mutatis mutandis:
$ gem install haml
$ mv main.css main.scss
$ sass --watch main.scss:main.css
(Except I had to mess around installing Ruby and Ruby Gems first; I'm not a Rails developer.)
Once I had my naïve Sassy CSS file started, I was able to start simplifying.
Centralizing Color Definitions
The Tron theme I was starting from was basically a monochrome color scheme: any given color on the page may be a little lighter or darker, more transparent or opaque, but always the same hue. And yet there were dozens of color definitions scattered throughout the file! It was particularly painful for me because I was working from a reference photo, and had to adjust the hue several times to get everything to look "right."
So the first thing I did was use a variable to define my colors at the top of the file:
$main-color: rgb(21,171,195); // blue-green
$highlight-color: rgb(36,198,224);
$code-color: rgb(39,215,243);
That let me search and replace and get a lot of definitions in one place, but we can take it a step farther, and using SASS color manipulations to base everything off of a single master color:
$main-color: rgb(21,171,195);:// blue-green
$highlight-color: lighten($main-color, 20%);
$code-color: lighten($main-color, 25%);
Which is OK because it's a monochrome color scheme and those colors really are logically tied together.
I'm also using a good deal of transparency, but there I'm able to the fade_out
function to set opacity independently of the color:
background-color: fade_out($highlight-color, 0.7);
This is awesome: if I decided it should look more like the green-screen consoles of yore, I could change every color on the page by editing a single line:
$main-color: green;
See?
(Of course, image colors don't change; that's outside the scope of CSS, but that's easy enough to fix with the Adjust Hue tool after the page looks "right.")
Glow Mixin
Lot's of things in the design "glow," and this is achieved with box shadows set to a bright color and a 0 offset. Unfortunately, you currently have to repeat yourself 3 times before all browsers understand you when you talk about box shadows:
-moz-box-shadow: 0 0 5px rgb(21,171,195);
-webkit-box-shadow: 0 0 5px rgb(21,171,195);
box-shadow: 0 0 5px rgb(21,171,195);
Ugh.
SASS has a solution for these kinds of problems though, called a mixin. A mixin lets you define a block of rules that you can re-use later.
Looking through the legacy CSS, I saw that I was using different radiuses and colors for different elements, but always the same 0 0
offset, so my glow mixin took two parameters, radius and color:
@mixin glow($radius, $color) {
-moz-box-shadow: 0 0 $radius $color;
-webkit-box-shadow: 0 0 $radius $color;
box-shadow: 0 0 $radius $color;
}
The mixin payed for itself right away because I was able to use it six times:
@include glow(9px, $main-color);
@include glow(10px, $highlight-color);
@include glow(5px, $main-color);
@include glow(10px, fade_out($main-color, 0.5));
@include glow(10px, $highlight-color);
@include glow(5px, $main-color);
Every single one of those uses was previously a block of three completely redundant lines. Obviously, the readability is hugely improved, as is the ability to rapidly experiment with different variations without having to worry about keeping everything in sync.
Roundy Plugin
The other visual element I was using over and over again was rounded corners. This also suffers from the tower of Babel problem, so I wrote a simple mixin for it too:
@mixin roundy($radius) {
-moz-border-radius: $radius;
-webkit-border-radius: $radius;
border-radius: $radius;
}
Mixins are just a huge DRY win. With raw CSS, the only way to get any kind of re-use out of a style was to give its own class and then tag the elements you want to look that way with the class; that's what I was doing before with the .roundy
class. But that's terrible, because it moves the decision up into the markup instead of leaving it in the stylesheet.
Analysis
The simple truth is that raw CSS doesn't provide any centralization or re-use. That's probably a good thing; keeping the CSS standard simple and trivially machine readable has probably helped browsers implement it consistently. But like assembly code, easily machine readable doesn't equate to easily human writable. CSS calls out to be machine generated. In fact, if you're minifying your CSS, you're already machine generating it!
So for my part, I'm drinking the Kool-aid and switching my development over to generated CSS. By sticking to tools like LESS or SASS that provide a superset of features on top of CSS, I'll make sure to stay close enough to CSS that I can debug and manipulate it in the browser, and it will be easy to migrate projects over... and if I ever change my mind, I can just take generated CSS, make it authoritative, and throw away the Sassy source files.
- Oran Looney January 29th 2011
Thanks for reading. This blog is in "archive" mode and comments and RSS feed are disabled. We appologize for the inconvenience.