Get Sassy – @elyseholladay – CSS is programming, too.



Get Sassy – @elyseholladay – CSS is programming, too.

0 1


get-sassy

Get Sassy — Thunder Plains Conf 10/7/13 - intro to Sass and refactoring/switching.

On Github elyseholladay / get-sassy

Get Sassy

hi, I'm

@elyseholladay

I'm a Designer/Developer/Princess of Sass at Square Root in Austin, TX. We build an application to help companies with franchises or dealerships manage their operations. I write our HTML and Sass front-end architecture, help create the design and UX for new features and products, and try to figure out how to run UI with 2 people and 4 off-shore Rails dev teams. I was previously at Bazaarvoice, if anyone has heard of them. I'm also a Girl Develop It and MakerSquare instructor.

CSS is programming, too.

How many of you are back-end developers? You write mainly JS..? What other languages? [audience participation time!] Cool—so you have nicely organized, commented Rails/Python/JS/what have you? Any designers in the room? Any of you work with designers? You/they have your perfectly named layered PSD files and a style guide and organized sprite files, right?

when you have to edit css

Fun Fact

Writing great CSS is not black magic!

It's not mysterious or incomprehensible; it's programming, just of a different kind. You can and should make good architectural choices when you write CSS. In terms of modern languages, CSS is kind of unsophisticated, so it's easy to make a mess. As our sites and apps get bigger and more complex, our CSS has grown, but the way we write it may not have.

Thankfully some smart people wrote Sass, which helps us write systematic, thoughtful code, organize it well, and even make better docs! So!

What is Sass?

What is Sass?

sass

/sas/

noun impudence; cheek; back talk.

verb being cheeky or rude to someone;to talk impudently to.

Not that...

Sass is CSS for CSS

In the same way that it was mind-blowingly awesome that in CSS we could declare two classes to have the same styles, Sass allows us to abstract out CSS code into variables, mixins, and functions, even do math, if and while statements, etc. Like real programming!

Anyone in here already using Sass? looking into using it?

This is Nathan Weizenbaum, the Sass developer, informing us as of April 2011, Sass is in fact a Turing Complete language. So it IS too programming!

this is not a .css file

Looks kinda like CSS, but it's a .scss file!

.sass vs .scss

Sass* (the language) has two syntaxes

* it's not an acronym!

.scss

SCSS* or "Sassy CSS" looks just like CSS.Every .css file is a valid .scss file.

                    
.widget {
   color: #fff; /* CSS works here */
   background: $backgroundColor; /* but so do Sass variables */
   margin: 20px;
   padding: 20px;
}
                    
                    

* it's an acronym!

.sass

.sass syntax uses indentation rather than brackets to indicate nesting of selectors, and newlines rather than semicolons to separate properties.

                    
.widget /* look ma, no brackets! */
   color: #fff /* or semi-colons! */
   background: $backgroundColor
   margin: 20px
   padding: 20px
                    
                    

.scss is a much easier place to begin, though, since it's a lot more forgiving and accepts all regular CSS.

I'm going to walk you through some of the basic functionality of Sass, first, so you can get an idea of what the language itself does. Then we'll talk about how to harness that power to switch over your projects.

Variables

                
$white: #ffffff;
$grey: #cccccc;
$lightpurple: #a18c9c;
$darkpurple: #694160;
$unit: 20px;
$unitless: 10;
                
                
Variables are a way to store a value, e.g. a color, number, string of text, or boolean, that you want to inject whereever that value would apply. It's easiest to understand with colors.

Variables

                
a, a:active, a:visited {
   color: $lightpurple; /* output: color: #a18c9c; */
   padding-bottom: $unit/2; /* output: padding-bottom: 10px; */
   font-size: $unitless+px; /* output: font-size: 20px; */
}

a:hover, a:focus {
   color: $darkpurple;
}
                
                

Everywhere I want #fff;, I can just write $white instead. It works with numbers, with or without units, too, and as you can see in this example, you can even do math on them.

Mixins

                
@mixin box {
   color: $white;
   background: $darkgrey;
   padding: $unit/2;
}

/* including a Mixin */
.box {
   @include box;
}
                
                

A mixin lets you make groups of CSS declarations that you want to reuse throughout your site. You can even pass in values to make your mixin more flexible.

This makes it easy for you to define a style (in this example, a box style) and include it in every div/class you want to have that style.

Mixins

                
/* Output */
.box {
   color: #ffffff;
   background: #222222;
   padding: 10px;
}
                
                

However, it does duplicate those lines every time, so if we used our box mixin twenty times, we'd have 60 lines of code.

Mixins are especially valuable for css3, like gradients, where you have many lines and don't want to write them all over and over. Browser-prefixes are a pain in the ass and mixins removes all that pain for you.

Mixins with Arguments

                
/* Mixin with a single argument */
@mixin border-radius-all($radius) {
  -webkit-border-radius: $radius;
     -moz-border-radius: $radius;
      -ms-border-radius: $radius;
       -o-border-radius: $radius;
          border-radius: $radius;
}

/* Mixin with multiple arguments */
@mixin border-radius($top-left, $top-right, $bottom-right, $bottom-left) {
  -webkit-border-radius: $top-left $top-right $bottom-right $bottom-left;
     -moz-border-radius: $top-left $top-right $bottom-right $bottom-left;
      -ms-border-radius: $top-left $top-right $bottom-right $bottom-left;
       -o-border-radius: $top-left $top-right $bottom-right $bottom-left;
          border-radius: $top-left $top-right $bottom-right $bottom-left;
}

                
                

Mixins can have no arguments, a single argument, or multiple arguments - you create scoped variables for them and then in your include, put the values you want.

Including Mixins

.box {
   @include box;
   @include border-radius-all(10px);
}

.button {
   @include border-radius(0, 2px, 0, 2px);
}
                
                

Mixin Output

                    
.box {
   color: #ffffff;
   background: #222222;
   padding: 10px;
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  -ms-border-radius: 10px;
  -o-border-radius: 10px;
   border-radius: 10px;
}

.button {
  -webkit-border-radius: 0 2px 0 2px;
  -moz-border-radius: 0 2px 0 2px;
  -ms-border-radius: 0 2px 0 2px;
  -o-border-radius: 0 2px 0 2px;
   border-radius: 0 2px 0 2px;
}
                
                

@extend

@extend is an easy way for one selector to share the styles of another selector, without duplicating the lines of CSS in your output.

                
.box {
   color: $white;
   background: $darkgrey;
   padding: $unit/2;
}

.widget { @extend .box; }
.sprocket { @extend .box; }
                
                
                    
.box, .widget, .sprocket {
   color: #ffffff;
   background: #222222;
   padding: 10px;
}
                
                

Where mixins use @include and literally include the lines of code everywhere you write @include, @extend's output comma-delineates the classes. Instead the same 4 lines of CSS duplicated 3x, we have 4 lines.

http://codepen.io/elyseholladay/pen/CyEqs

%extend

Placeholder %extend takes this one step further, and eliminates the output for the original ruleset.

[read before slide] Sometimes you’ll write styles for a class that you only ever want to @extend, and never want to use directly in your HTML. For our box styles, if that's visual only and the class .box never appears in our HTML, why output it into our CSS if it will never get applied? If you use normal classes and use @extend for this, you end up creating a lot of extra CSS when the stylesheets are generated. [now read slide]

                
%box {
   color: $white;
   background: $darkgrey;
   padding: $unit/2;
}

.widget { @extend %box; }
.sprocket { @extend %box; }
                
                
                    
.widget, .sprocket {
   color: #ffffff;
   background: #222222;
   padding: 10px;
}
                
                

So here you can see it's just removed class .box from the output. Not a major decrease here but if you do this a lot, then you will see much less clutter in your CSS. This is especially helpful for visual helpers, like typography classes, which I'll show you later.

Math Operators

Sass has standard math operators like +, -, *, /, and %

It also has equality operations == and != and relational operations > >= < <=.

+ can also be used to concatenate.

Sass has a handful of standard math operators like addition, subtraction, multiplication, division, and modulo (%). It also has equality operations, double equal == and not equal !=, and relational (greater than, less than, etc). Plus sign can also be used to concatenate.
                        
$unit: 20;
$big: 40;

.test {
    width: 100 + 200 - 20; /* addition, subtraction */
    height: 20 % 2000 + em /* modulo */
    margin: $unit*2+px /* unit variable, multiplication */
    @if $unit == 20 /* if statement, equality */
        padding: $unit+px /* + for concatenation */
    @else if $big >= 40 /* else if statement, relational */
        padding: $big+px
}

/* Output */
.test {
   width: 280;
   height: 20em;
   margin: 40px;
   padding: 20px;
}

                        
                    

Control Directives

Sass also has @if statements, @for loops, and @each and @while controls.

As you can see in the previous example, Sass also has if, else if, for, each, and while control directives that allow you to write some pretty complex controls for your CSS.

If statements, etc typically used when building frameworks to allow for doing something like a responsive grid, where there's quite a bit of math involved.

@if
                        
  $base-color: $black;

  .foo {
    @if $base-color == $black
        color: $white
    @else
        color: $black
  }

  /* Output */
  .foo {color: white;}
                        
                    

Simple if statements in Sass test can test against variables. You can do multiple conditions, but something simple like this is what I have seen the most. I use this to test against a $responsive-test variable; if it is true, I display responsive layout grid backgrounds and border colors for ease of testing. That way, instead of commenting in/out a bunch of code, I just toggle one variable true or false.

Color & Opacity Functions

lighten($color, $amount)

darken($color, $amount)

desaturate($color, $amount)

opacify($color, $amount)

transparentize($color, $amount)

rgba($color, $alpha)

ie-hex-str($color)

Sass has a ton of built-in functions. Here are some of the color-editing ones that are the most common: lighten, darken, desaturate, opacify (make more opaque by $amount), transparentize (make more transparent by $amount), rgba (change the alpha value of a color), and ie-hex-str, which converts a color to an IE friendly value (eg rgba to hex)

Number Functions

percentage($value)

type-of($value)

unit($number) & unitless($number)

round($value)

min($numbers…) & max ($numbers…)

Sass also has number functions, such as percentage, which converts a unitless number to a perctange; type-of returns the type of a value, and unit and unitless returns the unit associated with a number or if it has units, respectively. Sass can also do round, floor, min and max, etc.

Media Queries

Media queries in Sass behave the same as in CSS, except they can be nested inside a CSS rule.

                    
.sidebar {
   width: 300px;
   @media screen and (min-width: 1000px) {
      width: 500px;
   }
}
                    
                    
Output (same as CSS)
                    
.sidebar {
   width: 300px;
}

@media screen and (min-width: 1000px) {
   .sidebar {
      width: 500px;
   }
}
                    
                    
"@media directives in Sass behave just like they do in plain CSS, with one extra capability: they can be nested in CSS rules. If a @media directive appears within a CSS rule, it will be bubbled up to the top level of the stylesheet, putting all the selectors on the way inside the rule. This makes it easy to add media-specific styles without having to repeat selectors or break the flow of the stylesheet."
Named Media Queries

You can also name your breakpoints with a mixin...

                    
@mixin breakpoint($point) {
  @if $point == large {
    @media (max-width: 1600px) { @content; }
  }
  @else if $point == medium {
    @media (max-width: 1250px) { @content; }
  }
  @else if $point == small {
    @media (max-width: 650px)  { @content; }
  }
}
                     
                    

This is another good example of a Sass if statement; here we are defining a mixin with an argument of $point, and then saying, if my $point value that gets passed in my include is large, then print out this mediaquery, and the @content of it inside it.

So you can do this magic:
                    
.page-wrap {
  width: 75%; /* all sizes fallback */
  @include breakpoint(small) { width: 95%; } /* 0 to 650px */
  @include breakpoint(medium) { width: 80%; } /* 650px to 1250px */
  @include breakpoint(large) { width: 60%; } /* 1250px to 1600px */
}
                     
                    

Example stolen directly from Chris Coyier's CSS-tricks Named Media Queries article.

This is a much nicer way to author mediaqueries; it makes it easy to see what styles are related to what MQ size, you never have to write the #s again, or go searching through 2 different files to debug styles.

Importing

In CSS, @import allows you to import multiple stylesheets, but each stylesheet loads a separate HTTP request.

Sass @import can compile multiple Sass files, allowing you to use variables and mixins from other files, and only make one HTTP request.

[read slides]

Nesting

                
/* SCSS */
.about {
   margin: 20px auto;

   .column {
      float: left;
      width: 50%;
   }
}  
                
                

Last but not least, my least-favorite favorite Sass feature: nesting. You can nest a declaration inside another, and the output appends the classnames together.

Nesting

                
/* Output — same as CSS */
.about {
   margin: 20px auto;
}

.about .column {
   float: left;
   width: 50%;
}  
                
                

This gets super helpful for namespacing modules—you can ensure in your ouput that the classes are namespaced inside a parent class, in this case .about, without having to literally write .about .column, .about .whatever over and over.

This can get really hairy if you do it too much, but used sparingly it's very cool. THINK before you nest: why would you NEED to? What does your output look like?

But I already know CSS!

A lot of people are very resistant to the idea of a CSS preprocessor; why learn another tool, if you already are comfortable with the one you know and it works for you?

So why use Sass?

A lot of people recognize the power Sass can offer but mostly it seems like more work, more things to learn, more complicated language, etc etc. But there's a lot of reasons to switch that are more subtle than "ooh, mixins in CSS!" Even though you see tons of shiny, cool animation effects on CodePen using Sass, I would argue that is the least common use of Sass, and may exist only on CodePen.

Do you have...

One enormous stylesheet?

Organization only via comments?

Bananas selectors?

No consistent naming strategy?

Unpredictable bugs?

Do you have one giant CSS file, organized only by comments? Is it clear where you should add a new section or code? Do you spend a lot of time searching through it? Bananas selectors with no consistent naming strategy? Is the cascade actually hurting you because you're inheriting styles you don't actually want?

How many times do you have !important in your CSS?

Funny cause it's true.. but Sass will help you make and stick to a plan that will make your life SO much easier.

Plan for reusability, modularity, and efficiency.

Create a System

Jina Bolton, in her Sass presentations, says "create systems, not pages."

How can you make a framework of code that is reusable and modular and separate from individual pages? Instead of "style the homepage", style the modules that are on the homepage.

Your code should be reusable in as many places as possible, which makes it more efficient to write and debug.

If anyone is familiar with SMACSS, here's a somewhat SMACSS-based setup for CSS project architecture.

Library & Utility CSS

Anything that has little to no CSS output but is required everywhere.

resets, themes, colors, typefaces and sizes, mixins (css3, clearfix), grid, grid or mediaquery plugins

Start with your variables, for colors, typefaces and sizes, helpers such as mixins for CSS3, reset CSS if required, any theming, and grid/mediaquery code or plugins such as Susy, Breakpoint, Compass, etc. any @import includes.

Modules

Core shared styles that map to the DOM; modules that can appear on any page and should always look the same.

header, footer, sidebar, forms and inputs, global avatar or profile styles, buttons, error messaging

[read slide]
Structure & Style

Individual page structure, fancy layout tricks, images, any one-off styles.

[read slide] Or if you don't have a lot of reusable modules, you may only have individual pages, like on your personal site it might be homepage, about, and contact pages, but the blog posts styles are really the only reusable module. That's OK too—solve for the problem you are presented with.
but

I have to use the command line!?

Yes, to install Sass you have to install it via Rails in the command line. The really long and difficult command you have to write is as follows:

gem install sass

or use Codekit

That may not apply to you JS devs as much—you probably aren't as scared of the command line as a lot of designers—but either way, I would suggest using CodeKit: CodeKit helpfully compiles your stylesheets for you into regular or minified CSS, and has this helpful Live Reload option, where the page in your browser will automatically refresh itself!
but

won't my code be bloated & horrible!?

Yes, nesting CAN make for output like this*

.dashboard_tile .tile_content .tile_chart .tile_number.negative { color: red; }

* in the spirit of full disclosure, this is was—I fixed it!—from my code at work. please don't boo me off stage.

This is why you don't nest things in Sass unless they NEED to be. In this case, we really could eliminate the middle two classes—they aren't necessary. However I do want to nest at least the last 2 inside a parent, so that I can namespace and never worry about accidentally using that classname elsewhere and getting a conflict.

You get bloated output when you write bad CSS, when you nest when you don't need to, or when you over-use mixins or extends that aren't necessary. Read your output! YOU as an author are responsible for the output of your code, the exact same way as you are when you write plain CSS.

Never go more than three levels deep

For nesting, follow the inception rule.

Also, in terms of performance and reducing size to the browser, you'll do FAR more by reducing your JS and images than you will reducing your CSS selectors.

"If You Want to Learn Sass, Start By Not Using Sass"

Tom Genoni wrote a blog post about switching to Sass, in which he says that the best way to learn Sass without getting the insane output is to not use the crazy functionality in Sass.

"Sass is not a replacement for CSS, it’s more like having a CSS assistant who will help you write your code. So when you’re ready to really put it to work I recommend occasional sanity checks on the resulting CSS to see if this “assistant” has created too much repeated code or if the selectors are getting too complicated. Refactoring your Sass will help keep your code clean and you’ll start learning how you can make the best use of it."

I personally think using variables and mixins for obvious things you will reuse (like css3, clearfix, etc) is helpful, but if you are unsure about some of the extend or nesting or functions, don't use them--keep an eye on your code. This is an authoring tool and it's supposed to make your life BETTER, not harder.

http://atomeye.com/sass-and-compass.html
but

when can I switch?

now.

the next time you have to make a CSS change.

the next time you hear this:

"I don't like the shade of blue we are using in all our buttons/links/headlines. Can we change it?"

"Ugh, this CSS file is a total clusterfuck."

"Why is this text 18px on one page, 20px on another, and 16px on another??"

but

what about my legacy codebase!?

I don't know about y'all, but I don't work on sites for clients where at the end of a client's site I can start the next one from scratch and use Sass next time.

I work on a product, an app, and I don't get to just delete my entire CSS folder and do it all over. So what about my legacy codebase? Switching to Sass is going to be HARD! I don't have time for a project that big!

It's ok! I'll walk you through it step by step, and I'm going to use some code from work as a little bit of an example.

Steps to switch to Sass
Install CodeKit change all your .css files to .scss drag your project to Codekit & hit "compile" Open your site in a browser

Good job.

Ok, but seriously, there's more to it than that — let's walk through some easy ways to actually harness the power of Sass in your project that STILL don't require starting from scratch.

Step One

Refactoring without changing anything

The first thing you can change are things that don't actually change the structure of any of your CSS.

What reusable pieces can you pull out of your code that you know won't make anything break? Colors, type, and basic mixins are the easiest place to start.

Variables

cmd(+opt)+F in Sublime Text is your new BFF.

Color variables will make you more consistent both visually and in your CSS, and you'll never have to search for a hex color code in your CSS ever again.

Make a new file: colors.scss Make a new variable: $white: #ffffff; Search for all the #ffffff, #fff, #FFF, white, maybe even #f9f9f9. Replace them with $white Now do the rest!

Now is a really good time to take a little inventory of the UI of your app. What _should_ be white but is actually a super light grey? Do you have two VERY similar colors of blue? Can you consolidate some of your colors as you go to make things more efficient?

Use color operations to make variations.

Here I'm using Sass' built in color operations to make a light grey that is just a lighter version of our dark grey, rather than defining a new hex value. I'm also using transparentize to make an rgba color for transparent text, rather than defining another rgba color.

Sass has a ton of these: lighten, darken, saturate, desaturate, invert, complement, adjust hue, alpha (returns alpha value), opacify/transparentize (not kidding, real names- makes more or less opaque), even "change-color" to change any property of a color: red, green, blue values, hue, saturation, lightness, and alpha.

http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html

Typography Variables

                    
  // TYPEFACE VARIABLES
  $fontHeadline: "Proxima Nova Bold", "Calibri", Arial, sans-serif;
  $fontBody: "Calluna", "Cambria", Georgia, sans-serif;
  $fontSecondary: "Proxima Nova Light", "Calibri", Arial, sans-serif;
                    
                    

Here I've defined a headline font, a body font, and a "secondary" font, in case we ever wanted to use that. Like colors, this allows us to change everything in only one place.

typography variables
                    
  body {
    background: $white;
    font-family: $fontBody
    color: $greydark;
  }

  h1, h2, h3, h4, h5 {
    line-height: 1.2em;
    font-family: $fontHeadline;
    color: $highlight_color_bright;
  }
                    
                

So here I am pointing our body font to $fontBody, and our headlines to $fontHeadline. Note that it's possible to point the $fontBody variable or vice versa in your variable declarations, if you only wanted one font. I'd still define 2 variables, and use them appropriately: for an h2, I'd use $fontHeadline; if it was in the body or a paragraph declaration, I'd use $fontBody. Even though $fontBody points to $fontHeadline, let's imagine change it later. If you used $fontHeadline in the body declaration, and changed the $fontBody variable, your body font wouldn't change.

it works for font-size too

Harry Roberts of CSS Wizardry wrote an article called "Pragmatic, practical font sizing in CSS" and in it he came up with this greek lettering method of naming font sizes. Instead of ONLY defining your font sizes on h1, h2, h3, etc, we also define it on classes alpha, beta, etc. "So now .alpha can carry the style information of a h1 wherever you wish; it doesn’t depend on location or a type of element."

I took that one step further with Sass, and used the new placeholder extend functionality. Now I don't have to have classnames in my HTML, but I can extend them in my Sass, like this:

font-size extends
                    
  .notification {

      &:hover { background: lighten($tertiary_color,25%); }

      // read state of a notification
      &.read { background: transparentize($tertiary_color,75%); }

      .icon_arrow_right {
          @extend %milli;
          color: $greylight;
      }

      .icon_notification {
          @extend %delta;
          color: $primary_color_bright;
          padding: 5px 10px 0 0;
          float: left;
      }

      .notification_title {
          padding: .75em .5em;
          border-bottom: 1px solid $greypale;
          @extend %zeta;
      }   
  }
                    
                

Just @extend %delta, and you'll get 18px, without having to put the classname in your HTML. Additionally, from the content in the typography.sass file, h4 is output, but %delta itself isn't. You could take that one step further and eliminate the h1-6 selectors and ONLY use the placeholders, if you wanted. This works really well for me at work, because in our complex app, we prefer to use unique classnames over HTML selectors, to eliminate cascade issues. For clarity, I also like to not have non-informational classnames in my HTML, like, .delta. If you are working on your own blog or something smaller, it probably isn't as necessary to either eliminate this much CSS output, or avoid the classnames, but whether or not you use placeholder or classes, this is still a very valuable organization technique.

You should be able to do this without really changing much of your output CSS. If you go through the places in your codebase that you have font-size declarations, the same as we did for color variables, and replace with @extend .delta (whether that's a class or placeholder), you'll get the exact same output. If you are adding line-height to your typography.scss file you might have to be a bit more careful. Now you also have consistency going forward.

how many times have you written

the clearfix?

                    
.widget:after {
    content: "";
    display: block;
    height: 0;
    clear: both;
    visibility: hidden;
}
                    
                    
                    
.clearfix:before,
.clearfix:after {
    content: " ";
    display: table;
    *zoom: 1;  /* For IE 6/7 only: Include this rule to trigger hasLayout and contain floats. */
}

.clearfix:after {
    clear: both;
}
                    
                    
micro clearfix mixin
// CLEARFIX
// http://nicolasgallagher.com/micro-clearfix-hack/
// include @mixin clearfix on the parent div you want to clear
@mixin clearfix {
    zoom: 1; // For IE 6/7 (trigger hasLayout)

    &:after,
    &:before {
        content: " ";
        display: table;
    }

    &:after {
        clear: both
    }
}

                    

An easy mixin to start with is the clearfix; you probably use it everywhere, and there's a few lines of code you can remove from your stylesheets that really add up.

Already this is MUCH easier to read!

CSS3 Mixins

If you regularly use gradients, rounded corners, transitions, box or text shadow, or other complex CSS3 declarations, abstract them out into mixins.

This is what some of mine look like at work! I have some with single arguments, some with multiple, and some with "variable arguments" which are the ones with the ... at the end, to allow for unknown numbers of variables.

Putting it all Together

At the top of your main SCSS file, @import your new color, type, and mixin files. The rest of the file is exactly the same as it was before.

Your site should not visually have changed at all—you just replaced some code with something easier for you to author!

File Organization

Break out sections of your code that are related: errors, buttons, icons, sidebar, header, footer, etc.

Make a new file for each with a comment explaining what is in the file.

Goal is to keep files small and readable.

[read slide - 3 bullets]

Here we are still aiming to have no (or at least very little) visual change on the site. By doing this we just want to group our files, NOT start changing our CSS, classnames, etc.

Group & Import

Here's how I do it on my blog.

Group your new files into folders based on their type.

Your original CSS file should now be empty! Fill it back up by using @import to pull in all of your newly created files.

This is how I group mine at work, which is a bit more complex—and has better commenting!. There are lots of ways, and there's no limit to the number of folders you can have, as long as it makes sense for your app/site's files and CSS structure.

autocompile &live reload

CodeKit will automagically compile all of this stuff for you as you save/change files. If you're running a Rails app, including the Sass gem will do this on page refresh, but not livereload.

Step Two

Refactoring Some Actual CSS

So now that we have done that, our files are a bit easier to read, but what's in those files may still be clunky and messy. We'll still be getting the bugs we were before, have classnames that need to be cleaned up, etc. So let's talk about refactoring that actually changes your CSS, and maybe even HTML.

why do you refactor?

you have new requirements

the design or UI needs to change

code smell

bugs!?

[read slides]
refactoring is

changing the structure of existing code without changing its behavior

another great quote from jina bolton, in her realigning and refactoring talk. We refactor not to delete everything and start over, but to change the way the code is organized, change classnames and markup to be cleaner, without changing the end result.

you refactor for

clarity maintainability efficiencyDRY

We want our CSS to be clear, easy to read, easy to maintain; efficient to debug and to write, and to be DRY. Refactoring should improve upon one of those goals, if not all of them at once.

For example: if your CSS (sass or not!) matches your markup too much, you want to remove or consolidate your nesting or chained classes: refactor. If it may be cleaner and easier to use placeholder extend for a class you aren't using in HTML: refactor. If you have a bunch of classnames that should be a group but aren't: refactor.

Chris Eppstein, "Refactor My Stylesheets: The Digg.com Edition"

Chris Eppstein, a Sass author, did a refactor of Digg's Feedback/voting stylesheet to see how they could benefit from Sass. This was in 2010—and he mentions a few features Sass didn't have then, like placeholder extend, that they do now that could improve on this even more.

link
Steps to Refactor
Extract partial Find repeating patterns Create/extract base class Apply nesting Create mixins/extends

Extract partial

"I cut the related styles and pasted them into a new partial stylesheet named _feedback.scss. Then I inserted @import "feedback"; in its place in global.scss. Now I was able to focus on a single set of related styles."

/* This file started on line 2338 of global.css */
.confirm,
.error,
.warning,
.warningPersistant,
.info,
.positive,
.notice {
    color: #333;
    padding: 0.7em 5em 0.7em 4.3em;
    margin: 1.2em 0 1em 0 !important;
    clear: left;
} /* ie6 */ 

.confirm,
.positive,
.notice { 
    background: #eff6e8 url(/img/circle-check-green.gif) 1.3em 0.5em no-repeat; 
    border-top: 1px solid #A5CC7A; 
    border-bottom: 1px solid #A5CC7A;
}

.warning,
.warningPersistant, {     
    background: #fff url(/img/circle-yellow-exclamation.gif) 1.3em 0.5em no-repeat; 
    border-top: 1px solid #ddd; 
    border-bottom: 1px solid #ddd;
    }

.error {     
    background: #fff url(/img/circle-red-exclamation.gif) 1.3em 0.5em no-repeat; 
    border-top: 1px solid #ddd; 
    border-bottom: 1px solid #ddd;
}

.info {
    background: #fff url(/img/circle-yellow-info.gif) 1.3em 0.5em no-repeat;
    border-top: 1px solid #ddd;
    border-bottom: 1px solid #ddd;
}

.positive div,
.confirm div,
.error div,
.warning div,
.warningPersistant div,
.info div
.notice div {
    display: inline;
    background: none;
    padding: 0;
    margin: 0;
}

.positive p,
.confirm p,
.error p,
.warning p,
.warningPersistant p,
.info p
.notice p {
    margin: 0;
}

.positive h3,
.positive strong,
.confirm h3,
.contirm strong,
.warning h3,
.warning strong,
.warningPersistant h3,
.warningPersistant strong,
.error h3,
.error strong,
.info h3,
.info strong {
    font-weight: bold !important;
    letter-spacing: normal !important;
    font-size: 1.2em !important;
    padding: 0 0.5em 0 0 !important;
    margin: 0 !important;
    display: inline;
}

.positive h3,
.positive strong,
.confirm h3,
.contirm strong {
    color: #e18015;
}

.warning h3,
.warning strong,
.warningPersistant h3,
.warningPersistant strong {
    color: #b50b05;
}

.error h3,
.error strong {
    color: #b50b05;
}

.confirm:hover {
    color: #030;
}

h2 .confirm {
    font-size: 50%;
    float: right;
}

.instruction {
    background: #EAF2FA url(/img/feature-box.gif) 0 0 no-repeat;
    margin-bottom: 1em;
    color: #6C7D8E;
}

.instruction div {
    background: url(/img/feature-box.gif) 100% 100% no-repeat;
    padding: 15px 15px 10px 15px;
}

.instruction h3 {
    color: #6C7D8E;
}
                    

Find repeating patterns

"...a large amount of selector duplication that implied an inheritance relationship, ...a repeating pattern relating to the colors and iconography, ...[and] some complex nesting of selectors."

"There is a lot of duplication in CSS when you use semantic markup. It can be challenging to identify the different types of duplication. First and foremost, I saw a large amount of selector duplication that implied an inheritance relationship. I looked for a CSS class that could function as the base class and found none. I also saw a repeating pattern relating to the colors and iconography. Lastly, there was some complex nesting of selectors."

Create/extract base class

"I decided to call it .feedback because all of the class names in use described a type of user feedback and this class was not in use already."

here is where he says "Unfortunately, Sass does not yet have any notion of an abstract base class, so this cleanup will incur some cost of additional output of a style they don’t want or need." which is exactly what placeholder extend is.
.feedback {
  color: #333;
  padding: 0.7em 5em 0.7em 4.3em;
  margin: 1.2em 0 1em 0 !important;
  clear: left;
}
 
.confirm,
.positive,
.notice { 
    @extend .feedback;
    background: #eff6e8 url(/img/circle-check-green.gif) 1.3em 0.5em no-repeat; 
    border-top: 1px solid #A5CC7A; 
    border-bottom: 1px solid #A5CC7A;
}
                    

Apply nesting

"Much of the duplication required when styling semantic content is due to styling nested content. ... I’m using Sass’s parent-reference selector & with the styles for h3, strong."

"Much of the duplication required when styling semantic content is due to styling nested content. Sass’s ability to nest selectors makes this stylesheet much easier to read and understand what’s going on. Note how I’m using Sass’s parent-reference selector (&) with the styles for h3, strong. The intent of that block is to style those elements, so I have inverted the nesting order to give more clarity to the intent of those styles."

h3, strong {
    .feedback & { /* Output is: .feedback h3, .feedback strong, etc */
        font-weight: bold;
        letter-spacing: normal;
        font-size: 1.2em;
        padding: 0 0.5em 0 0;
        margin: 0;
        display: inline;
    }
    .positive &, .confirm &
        { color: #e18015; }
 
    .warning &, .warningPersistant &
        { color: #b50b05; }
 
    .error &
        { color: #b50b05; }
}
 
.confirm {
    &:hover { color: #030; }
    h2 & {
        font-size: 50%;
        float: right;
    }
}
                    

Create mixins/extends

"The last major source of duplication ... is the common styling pattern for colors and iconography. To simplify this we extract a mixin and apply it wherever the pattern is in use."

@mixin feedback-appearance($bg-color, $icon, $border-color) {
    background: $bg-color url('/img/circle-#{$icon}.gif') 1.3em 0.5em no-repeat; 
    border-top: 1px solid $border-color; 
    border-bottom: 1px solid $border-color;
}
 
.confirm,
.positive,
.notice { 
    @extend .feedback;
    @include feedback-appearance(#eff6e8, "check-green", #A5CC7A);
}
 
.warning,
.warningPersistant {     
    @extend .feedback;
    @include feedback-appearance(#fff, "yellow-exclamation", #ddd);
}
 
.error {     
    @extend .feedback;
    @include feedback-appearance(#fff, "red-exclamation", #ddd);
}
 
.info {
    @extend .feedback;
    @include feedback-appearance(#fff, "yellow-info", #ddd);
}
                    
Results?

125 lines > 85 lines

"85 lines of code... down from 125 lines of code and providing the exact same output... a 32% reduction!"

"But the biggest win is that adding a new kind of feedback requires only 1 or 2 points of edit instead of the 5-7 that would have been required before. This is, without a doubt, more maintainable...

Then he says,"Additionally, we fixed six bugs without trying." "the defect rate in this tested, in-production stylesheet is evidence of how hard it is for even great front-end developers to maintain semantic CSS."

  Cool! so let's go through an example step by step—with some buttons.

Here we have an imaginary sampling of some buttons that you might see on a site. We have:

Save button a larger Sign Up button two edit buttons, one clearly disabled and slightly misaligned a delete button and a cancel link

visual issues
  • Save's drop-shadow is grey, not colored
  • Sign Up has wider letterspacing
  • Edit/Edit are different font weights
  • non-disabled Edit and Cancel link change to not bold on hover
  • Delete has no hover state
CSS issues
  • Redundancy of code
  • Some rgba, some hex value colors
  • Classnames have no consistent naming
  • Save button has 2 color values
  • Inconsistent capitalization in both HTML and CSS
...and more issues
Redundant lines of CSS Inconsistent margin/spacing Edit buttons use height/width (and are different), use border-box Other buttons use padding; only Sign Up uses line-height Inconsistent selector ordering Did I mention the redundancy?

Extract partial

Copy all the button-related CSS into a new file: buttons.scss, and include it in your main .scss file. Check to make sure everything is the same as it was.

Find repeating patterns

What parts of our button CSS can we reuse?

body {
  font-family: "Pluto Sans", Arial;
  font-size: 100%;
}

a, a:hover, a:active, a:visited {text-decoration: none; font-weight: normal;}

.save_button {
  display: inline-block;
  padding: 16px 22px;
  color: #fff;
  font-size: 24px;
  border-radius: 4px;
  text-transform: uppercase;
  color: #c6eff0;
  background-color: #2ba2a6;
  box-shadow: 0px 8px 0px 0px rgba(0,0,0, 0.125);
  border-right: 1px solid #217a7d;
  border-bottom: 1px solid #217a7d;
  text-shadow: 0px 1px 1px rgba(0,0,0,.25);
}

.save_button:hover {
  background: #217A7D;
}

.sign_up_button {
  margin-right: 10px;
  margin-left: 10px;
  background: #237cbe;
  box-shadow: 0px 8px 0px 0px rgba(35, 124, 190, 0.25);
  border: 1px solid #1b6093;
  text-shadow: 0px 1px 1px #0b283d;
  color: #fff;
  letter-spacing: 1px;
  font-size: 2em;
  line-height: 2.5em;
  padding: 20px;
  border-radius: 10px;
  text-transform: uppercase;
}

.sign_up_button:hover,
.sign_up_button:focus,
.sign_up_button:active {
  background: #1B6093;
  border-color: #0B283D;
}

.button-edit {
  display: inline-block;
  height: 65px;
  width: 110px;
  padding-left: 26px;
  padding-top: 16px;
  box-sizing: border-box;
  margin-right: 10px;
  text-transform: uppercase;
  font-size: 24px;
  border-radius: 4px;
  font-weight: bold;
  color: #d579cc;
  background-color: #993399;
  box-shadow: 0px 8px 0px 0px rgba(153, 51, 153, 0.25);
  border-right: 1px solid #732673;
  border-bottom: 1px solid #732673;
  text-shadow: 0px 1px 1px #260d26;
}

.button_edit:hover {
  background: #732673;
}

.button-disabled {
  display: inline-block;
  height: 65px;
  width: 110px;
  padding-left: 26px;
  padding-top: 18px;
  box-sizing: border-box;
  margin-right: 10px;
  text-transform: uppercase;
  font-size: 24px;
  border-radius: 4px;
  color: #888;
  background-color: #ccc;
  cursor: default;
  margin-right: 20px;
}

.button_delete {
  color: white;
  background-color: #e6475b;
  box-shadow: 0px 8px 0px 0px rgba(230, 71, 91, 0.25);
  border-right: 1px solid #dc1e36;
  border-bottom: 1px solid #dc1e36;
  text-shadow: 0px 1px 1px #821220;
  display: inline-block;
  padding: 16px 22px;
  border-radius: 4px;
  font-size: 22px;
  margin-right: 20px;
}

.cancel_button_link {
  color: black;
  font-weight: bold;
  font-size: .8em;
  text-transform: capitalize;
}

.cancel_button_link:hover {
  color: grey;
}
                    

This is the fun part! Audience participation time.. and go!

display: inline-block consistent padding and line height for height/width, not height/width or padding only box-shadow, text-shadow, border-radius CSS3 capitalization, using text-transform: uppercase and regular-case in HTML consistent hover/active/focus states consistent margin-right spacing consistent bold/unbolding

Create/extract base class

.button {
    display: inline-block;
    box-sizing: border-box;
    margin: 0 20px 0 0;
    padding: .25em 1em;
    border-radius: 4px;
    font-family: "Pluto Sans", sans-serif;
    letter-spacing: 1px;
    font-size: 24px;
    line-height: 2.6;
    text-align: center;
    text-transform: uppercase;
    text-decoration: none;
    cursor: pointer;
}

/* Button Types */

.save_button {
    color: #fff;
    background-color: #2ba2a6;
    box-shadow: 0px 8px 0px 0px rgba(0,0,0, 0.125);
    border-right: 1px solid #217a7d;
    border-bottom: 1px solid #217a7d;
    text-shadow: 0px 1px 1px rgba(0,0,0,.25);
}

.save_button:hover {
    background: #217A7D;
}

.sign_up_button {
    background: #237cbe;
    box-shadow: 0px 8px 0px 0px rgba(35, 124, 190, 0.25);
    border: 1px solid #1b6093;
    text-shadow: 0px 1px 1px #0b283d;
    color: #fff;
}

.sign_up_button:hover,
.sign_up_button:focus,
.sign_up_button:active {
    background: #1B6093;
    border-color: #0B283D;
}

.button-edit {
    color: #d579cc;
    background-color: #993399;
    box-shadow: 0px 8px 0px 0px rgba(153, 51, 153, 0.25);
    border-right: 1px solid #732673;
    border-bottom: 1px solid #732673;
    text-shadow: 0px 1px 1px #260d26;
}

.button_edit:hover {
    background: #732673;
}

.button-disabled {
    color: #888;
    background-color: #ccc;
    cursor: default;
}

.button_delete {
    color: white;
    background-color: #e6475b;
    box-shadow: 0px 8px 0px 0px rgba(230, 71, 91, 0.25);
    border-right: 1px solid #dc1e36;
    border-bottom: 1px solid #dc1e36;
    text-shadow: 0px 1px 1px #821220;
}

.cancel_button_link {
    color: black;
    font-size: .8em;
    text-transform: capitalize;
}

.cancel_button_link:hover {
    color: grey;
}
                    

So here we get to start doing some Sass! Let's pull out all of the CSS that is shared across all of the buttons, and leave only what is unique to each button.

Now we have some ugly buttons..

@extend .button

.button {
    display: inline-block;
    box-sizing: border-box;
    margin: 0 20px 0 0;
    padding: .25em 1em;
    border-radius: 4px;
    font-family: "Pluto Sans", sans-serif;
    letter-spacing: 1px;
    font-size: 24px;
    line-height: 2.6;
    text-align: center;
    text-transform: uppercase;
    text-decoration: none;
    cursor: pointer;
}

/* Button Types */

.save_button {
    @extend .button;
    ...
}

.sign_up_button {
    @extend .button;
    ...
}

.button-edit {
    @extend .button;
    ...
}

.button-disabled {
    @extend .button;
    ...
}

.button_delete {
    @extend .button;
    ...
}

.cancel_button_link {
    @extend .button;
    ...
}

                    
so let's extend that button class into each of our button declarations.. voila.

Extend Output

.button, .save_button, .sign_up_button, .button-edit, .button-disabled, .button_delete, .cancel_button_link {
    display: inline-block;
    box-sizing: border-box;
    margin: 0 20px 0 0;
    padding: .25em 1em;
    border-radius: 4px;
    font-family: "Pluto Sans", sans-serif;
    letter-spacing: 1px;
    font-size: 24px;
    line-height: 2.6;
    text-align: center;
    text-transform: uppercase;
    text-decoration: none;
    cursor: pointer;
}
                    

Apply nesting

.save_button {
    @extend .button;
    color: #fff;
    background-color: #2ba2a6;
    box-shadow: 0px 8px 0px 0px rgba(0,0,0, 0.125);
    border-right: 1px solid #217a7d;
    border-bottom: 1px solid #217a7d;
    text-shadow: 0px 1px 1px rgba(0,0,0,.25);
    
    &:hover,
    &:active,
    &:focus {
        background: #217A7D;
    }

}


.sign_up_button {
    @extend .button;
    background: #237cbe;
    box-shadow: 0px 8px 0px 0px rgba(35, 124, 190, 0.25);
    border: 1px solid #1b6093;
    text-shadow: 0px 1px 1px #0b283d;
    color: #fff;

    &:hover,
    &:active,
    &:focus {
        background: #1B6093;
        border-color: #0B283D;
    }
}

.button-edit {
    @extend .button;
    color: #d579cc;
    background-color: #993399;
    box-shadow: 0px 8px 0px 0px rgba(153, 51, 153, 0.25);
    border-right: 1px solid #732673;
    border-bottom: 1px solid #732673;
    text-shadow: 0px 1px 1px #260d26;

    &:hover,
    &:active,
    &:focus {
        background: #732673;
    }
}

.button-disabled {
    @extend .button;
    color: #888;
    background-color: #ccc;
    cursor: default;
}

.button_delete {
    @extend .button;
    color: white;
    background-color: #e6475b;
    box-shadow: 0px 8px 0px 0px rgba(230, 71, 91, 0.25);
    border-right: 1px solid #dc1e36;
    border-bottom: 1px solid #dc1e36;
    text-shadow: 0px 1px 1px #821220;
    &:hover,
    &:active,
    &:focus {
        background: darken(#e6475b,10%);
    }
}

.cancel_button_link {
    @extend .button;
    color: black;
    font-size: .8em;
    text-transform: capitalize;
    
    &:hover,
    &:active,
    &:focus {
        color: #666;
    }
}
                
I'm just going to nest the active/hover states, and make them consistent. Note that the disabled state doesn't get one--it doesn't need one! Now our hover/active/focus states are consistent and nested inside their parent button classes.

Create placeholder extend

%button {
    display: inline-block;
    box-sizing: border-box;
    margin: 0 20px 0 0;
    padding: .25em 1em;
    border-radius: 4px;
    font-family: "Pluto Sans", sans-serif;
    letter-spacing: 1px;
    font-size: 24px;
    line-height: 2.6;
    text-align: center;
    text-transform: uppercase;
    text-decoration: none;
    cursor: pointer;
}

/* Button Types */

.save_button {
    @extend %button; // Placeholder
    color: #fff;
    background-color: #2ba2a6;
    box-shadow: 0px 8px 0px 0px rgba(0,0,0, 0.125);
    border-right: 1px solid #217a7d;
    border-bottom: 1px solid #217a7d;
    text-shadow: 0px 1px 1px rgba(0,0,0,.25);
    
    &:hover,
    &:active,
    &:focus {
        background: #217A7D;
    }

}

...
                    
Next I want to change my .button class to a placeholder extend %button, so that it doesn't get output in my CSS. It doesn't eliminate many lines of code for us but since we don't want that class in our HTML anywhere, we can remove it from our CSS too.

Placeholder Output

.save_button, .sign_up_button, .button-edit, .button-disabled, .button_delete, .cancel_button_link {
  display: inline-block;
  box-sizing: border-box;
  margin: 0 20px 0 0;
  padding: .25em 1em;
  border-radius: 4px;
  font-family: "Pluto Sans", sans-serif;
  letter-spacing: 1px;
  font-size: 24px;
  line-height: 2.6;
  text-align: center;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
}
                    
Not a lot of difference here.. but .button isn't there anymore!

Change Classnames

<a href="#" class="save_button">Save</a>

<a href="#" class="sign_up_button">Sign Up!</a>

<a href="#" class="button-edit">Edit</a>

<a href="#" class="button-disabled">Edit</a>

<a href="#" class="button_delete">DELETE</a>

<a href="#" class="cancel_button_link">cancel</a>

                    

This is the portion that involves editing your HTML. We could stop here: we have made a lot of good changes toward reusability and modularity, so even if the classes of the actual buttons are all inconsistent, we have done a lot. But some good search-and-replace can get us to a modular setup that will help in the future, when we want to add even more buttons.

Use .button in HTML

<a href="#" class="button save_button">Save</a>

<a href="#" class="button sign_up_button">Sign Up!</a>

...
                    
.button {shared styles}
    &.save_button {unique styles}
    &.sign_up_button {unique styles}
                        
                    

Like I mentioned before, we could add button to our HTML and then nest all the unique button classes inside .button in our Sass file. But I don't like to do this—I like to remove classes.

Use shared classes

<!-- .save_button is now: -->
<a class="button_primary" id="button_save_form">Save</a>

<!-- .sign_up_button is now: -->
<a class="button_calltoaction" id="button_signup">Sign Up!</a>

<!-- .button-edit is now: -->
<a class="button_secondary" id="button_edit_profile">Edit</a>

<!-- .button-disabled is now: -->
<a class="button_secondary button_disabled" id="button_edit_profile">Edit</a>

<!-- .button_delete is now: -->
<a class="button_tertiary" id="button_delete_comment">Delete</a>

<!-- .cancel_button_link is now: -->
<a class="button_textonly" id="button_cancel_comment">Cancel</a>

                    

What I like to do is use the stylistic class in addition to a unique ID. I prefer an ID because it allows us a unique way to call each button in JS, but also stick to a consistent naming scheme: button_buttonTYPE.

Then I apply the stylistic class. I use button_primary, secondary, tertiary, etc. You could also do like Bootstrap: default, primary, success, info, warning, danger, link. Very similar. They also go a bit more OOCSS style and do .btn-lg, sm, xs, to change text sizes.

So now you'll notice that our disabled button is getting the secondary border/shadow styles. This is because we didn't define any for it. So let's go in and define those, so that this disabled state can apply to any button. What else did we miss? Hover state on disabled, and disabled state on the text only button. So let's add those.

Missing States

// Disabled button state - can apply to other