Truth be told, I hardly ever write plain CSS anymore (only if a project requires very little CSS or I’m updating an existing project without it.) Now Sass just feels more natural. It’s certainly more powerful and flexible. However, when I started updating this site I kind of wanted to write plain CSS for it…I soon regretted that decision.
Actually writing this group of selectors made me really reconsider my decision, yet I forged ahead.
.nav__link:active,
.nav__link:hover,
.nav__link:focus,
.wp-pagenavi > .page:active,
.wp-pagenavi > .page:focus,
.wp-pagenavi > .page:hover,
.wp-pagenavi > .previouspostslink:active,
.wp-pagenavi > .previouspostslink:focus,
.wp-pagenavi > .previouspostslink:hover,
.wp-pagenavi > .first:active,
.wp-pagenavi > .first:focus,
.wp-pagenavi > .first:hover,
.wp-pagenavi > .nextpostslink:active,
.wp-pagenavi > .nextpostslink:focus,
.wp-pagenavi > .nextpostslink:hover,
.wp-pagenavi > .last:active,
.wp-pagenavi > .last:focus,
.wp-pagenavi > .last:hover {
background-color: #fff;
}
So like two separate religions colliding into the sanctimony of marriage, it was time to convert.
Setup Compass
At this point Sass and Compass are synonymous to me; if I’m using Sass then I’m using Compass. There are just so many useful features included in Compass. In fact, Compass is the main reason I never really consider going back to Less.
I use Compass so often that I have a handy dandy bash alias for it.
alias compass_new='compass create --bare --syntax sass --sass-dir "src/sass" --css-dir "static/css" --javascripts-dir "static/js" --images-dir "static/img"'
This creates a new Compass project using the original Sass syntax, which is kept in ‘src/sass’ (in the directory you created the project) and converts it in the ‘static/css’ directory. I’m not using the javascripts-dir or images-dir right now, so those are just changed to my usual preference of a ‘static/*’ directory.
This creates a config.rb file, which you can easily adjust later. I actually changed the css-dir to be in the root of the theme in order to play nicely with WordPress.
In another terminal tab (I use iTerm) I type ‘compass watch’ and boom, Compass is effectively running.
@import Compass
You can import as much or as little of Compass as you’d like. In this case I just want to import the CSS3 and reset modules. So I add this to my partial file (I’ll get there):
@import compass/reset
@import compass/css3
By importing Compass’s built in reset module I can remove Eric Meyer’s reset from my code. The CSS3 module will just make including cross-browser CSS3 styles that much easier.
For instance, instead of writing:
-moz-border-radius-topleft: 10px;
-webkit-border-top-left-radius: 10px;
border-top-left-radius: 10px;
I can just write:
+border-top-left-radius(10px)
When converted to CSS is:
-moz-border-radius-topleft: 10px;
-webkit-border-top-left-radius: 10px;
border-top-left-radius: 10px;
Well, that looks familiar. Do I need the vendor pre-fixes at this point? Probably not, but why not include them when it's that simple?
Sass partials
Sass partials are Sass files beginning with an underscore in their name. Unlike regular Sass files these don't get converted to CSS when a directory is being watched.
Sass partials are the bee's knees, although I use them a lot more sparingly than some people. However, I do usually create a '_base.sass' file for each project. This is where I put most of my @imports and include useful, but not overly specific variables, functions or mixins.
I use some functions and mixins so often that I put together some that I wrote in a gist (although I need to add my responsive mixin in there.)
I'm not using any webfonts on this site, so there's no need to keep that part. That results in this Sass partial.
Converting
While all valid CSS is valid SCSS, I prefer the concise indented syntax, not to mention CSS is inherently missing the benefits of Sass.
Thankfully Sass has a built in sass-convert option.
sass-convert styles.css src/sass/styles.sass
Running that would have converted my styles to the indented syntax; however, I just did it myself in TextMate...it really didn't take too long.
Once my CSS was converted to Sass, I started going through and updating Sass to make it better and more efficient. I updated the CSS3 declarations to use includes from Compass, as demonstrated earlier.
I went through used some of my custom functions. For instance, this:
h1 {
font-size: 26px;
font-size: 2rem;
margin-bottom: .769em;
}
Was updated to this:
h1
+font($size: 26px)
margin-bottom: em(20px, $base: 26px)
That's using my custom mixin for fonts ('+' is conciser way of @import) and my custom function to turn pixels into ems. This outputs:
h1 {
font-size: 26px;
font-size: 2rem;
margin-bottom: 0.76923em;
}
And of course there's the power of nesting.
table {
border-collapse: collapse;
margin-bottom: 1.5em;
width: 100%;
}
table tr {
vertical-align: top;
}
table tr th {
background-color: #eee;
color: #111;
font-weight: bold;
padding: .25em;
}
table tr td {
padding: .25em;
}
Which is updated in indented syntax to:
table
border-collapse: collapse
@extend %base-vertical-rhythm
@extend %col-1
tr
vertical-align: top
th
background-color: $base-color-invert
@extend %base-color
+font($weight: bold)
padding: .25em
td
padding: .25em
And that snippet brings me to variables ($base-color-invert) and silent classes (@extend %base-vertical-rhythm).
Variables
One of the things that first piqued my interest in CSS pre-processors was variables.
// Project variables
// Colors
$base-color: #111
$base-color-invert: invert($base-color)
$base-color-invert-darken-6: darken($base-color-invert, 6.5deg)
$base-color-invert-lighten-6: lighten($base-color-invert, 6.5deg)
$base-color-lighten-13: lighten($base-color, 13.3deg)
$base-color-lighten-33: lighten($base-color, 33.3deg)
$base-color-link: rgb(110, 155, 220)
$base-color-link-hue-49: adjust-hue($base-color-link, 49deg)
$base-color-link-hue-120: adjust-hue($base-color-link, 120deg)
$base-color-link-hue-neg-71: adjust-hue($base-color-link, -71deg)
// Fonts
$base-font-size: 13px
$base-font-family-sans-serif: Arial, sans-serif
$base-font-family-serif: Georgia, 'Times New Roman', Times, serif
$base-line-height: 1.5
The color variables are kind of crazy, but since I already had my colors selected, I decided to use Sass' color math for funsies. I'll have to get into color math at another time, but the power of variables is self evident.
Silent classes
If Sass partials are the bee's knees than silent classes are the cat's fucking pajamas. Like partials, silent classes aren't output into CSS...that is until they are extended.
Remember that ridiculous ruleset from the beginning of this article, well I can easily achieve the same effective ruleset much easier.
%prominent-link
background-color: $base-color-invert-darken-6
border: 3px solid $base-color-lighten-13
+box-sizing(border-box)
@extend %base-color
display: inline-block
+font($weight: bold)
line-height: 39px
text-align: center
+transition(background-color .2s linear)
&:active,
&:focus,
&:hover
background-color: $base-color-invert-lighten-6
.nav__link,
.wp-pagenavi > .current,
.wp-pagenavi > .page,
.wp-pagenavi > .previouspostslink,
.wp-pagenavi > .first,
.wp-pagenavi > .nextpostslink,
.wp-pagenavi > .last
@extend %prominent-link
%prominent-link
is the silent class, which is extended with @extend. And since I have active, focus and hover nested underneath they are included as well.
If you're curious, this is the CSS output:
.nav__link,
.wp-pagenavi > .current,
.wp-pagenavi > .page,
.wp-pagenavi > .previouspostslink,
.wp-pagenavi > .first,
.wp-pagenavi > .nextpostslink,
.wp-pagenavi > .last {
background-color: #dddddd;
border: 3px solid #333333;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
display: inline-block;
font-weight: bold;
line-height: 39px;
text-align: center;
-webkit-transition: background-color 0.2s linear;
-moz-transition: background-color 0.2s linear;
-o-transition: background-color 0.2s linear;
transition: background-color 0.2s linear;
}
.nav__link:active,
.wp-pagenavi > .current:active,
.wp-pagenavi > .page:active,
.wp-pagenavi > .previouspostslink:active,
.wp-pagenavi > .first:active,
.wp-pagenavi > .nextpostslink:active,
.wp-pagenavi > .last:active, .nav__link:focus,
.wp-pagenavi > .current:focus,
.wp-pagenavi > .page:focus,
.wp-pagenavi > .previouspostslink:focus,
.wp-pagenavi > .first:focus,
.wp-pagenavi > .nextpostslink:focus,
.wp-pagenavi > .last:focus, .nav__link:hover,
.wp-pagenavi > .current:hover,
.wp-pagenavi > .page:hover,
.wp-pagenavi > .previouspostslink:hover,
.wp-pagenavi > .first:hover,
.wp-pagenavi > .nextpostslink:hover,
.wp-pagenavi > .last:hover {
background-color: white;
}
One problem with silent classes is that you can't use them inside a mixin, which is usually how responsive is handled. But I believe this is being worked on, and they're still fairly new.
Speaking of responsive, I'll quickly go over those changes.
Responsive
With the combined power of mixins and nesting, Sass makes responsive development that much easier. In my partial I have a responsive mixin:
// Mixin for media queries
@mixin respond-to($device)
@if $device == mobile
@media only screen and (max-width: 767px)
@content
@if $device == tablet
@media only screen and (min-width: 768px)
@content
@if $device == desktop
@media only screen and (min-width: 769px)
@content
On this site I'm only developing for mobile and then responding to "desktop." So I could delete the tablet part but it's not doing any harm. One thing I do need to optimize is change the sizes to a variable to be set later.
By using this mixin I can add the responsive style right along with the others - no more forgetting the original styles, or separate stylesheets or segregated media queries. For instance I no longer have to do this:
/* Main layout **/
#wrapper {
margin: 0 auto;
}
@media only screen and (min-width: 769px) {
#wrapper {
min-width: 59.154em; /* 769px */
max-width: 73.846em; /* 960px */
}
}
That same selector was referenced 331 lines later. Now I can just do this:
// Main layout
#wrapper
margin: 0 auto
+respond-to(desktop)
min-width: em(769px)
max-width: em(960px)
However, the output is something I'm less than thrilled about. It adds the media query where the selector is referenced, which means the media query is repeated several times in the CSS. This is something that's actively being worked on, and I'm sure it will eventually be much cleaner.
#wrapper {
margin: 0 auto;
}
@media only screen and (min-width: 769px) {
#wrapper {
min-width: 59.15385em;
max-width: 73.84615em;
}
}
La fin
And with your powers combined, I am Captain Planet...err wait, I mean and with that my CSS is converted to Sass, which is, um, converted back to CSS. But hey, it's worth it, especially when I can quickly change the Sass output style to compressed and instantly have a minified CSS file.
As always, the full code is available on GitHub if you're interested, and after reading this far, I hope you are.