Get Sassy

hi, I'm

@elyseholladay

CSS is programming, too.











when you have to edit css

Fun Fact

Writing great CSS is not black magic!

What is Sass?

sass

/sas/

noun impudence; cheek; back talk.

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

Sass is CSS for CSS

this is not a .css 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
                    
                    

Variables

                
$white: #ffffff;
$grey: #cccccc;
$lightpurple: #a18c9c;
$darkpurple: #694160;
$unit: 20px;
$unitless: 10;
                
                

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;
}
                
                

Mixins

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

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

Mixins

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

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;
}

                
                

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;
}
                
                

%extend

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

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

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

Math Operators

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

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

+ 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.

@if
                        
  $base-color: $black;

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

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

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)

Number Functions

percentage($value)

type-of($value)

unit($number) & unitless($number)

round($value)

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

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;
   }
}
                    
                    
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; }
  }
}
                     
                    
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.

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.

Nesting

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

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

Nesting

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

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








But I already
know CSS!

So why use Sass?

Do you have...

One enormous stylesheet?

Organization only via comments?

Bananas selectors?

No consistent naming strategy?

Unpredictable bugs?

Plan for reusability, modularity, and efficiency.

Create a System

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

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

Structure & Style

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









but

I have to use the
command line!?

gem install sass

or use Codekit







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.


















Never go more than
three levels deep

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








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!?

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










Good job.

Step One

Refactoring without changing anything

Variables

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

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


Use color operations to make variations.

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;
                    
                    
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;
  }
                    
                

it works for font-size too

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;
      }   
  }
                    
                
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
    }
}

                    

CSS3 Mixins

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

Putting it all Together

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.

Group & Import

autocompile &
live reload

Step Two

Refactoring Some Actual CSS

why do you refactor?

you have new requirements

the design or UI needs to change

code smell

bugs!?

refactoring is

changing the structure
of existing code without changing its behavior

you refactor for

clarity
maintainability
efficiency
DRY




Chris Eppstein, "Refactor My Stylesheets: The Digg.com Edition"
Steps to Refactor
  1. Extract partial
  2. Find repeating patterns
  3. Create/extract base class
  4. Apply nesting
  5. 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."

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."


.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."


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

 
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;
    }
                        

    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;
    }
                        

    @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;
        ...
    }
    
                        

    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;
        }
    }
                    

    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;
        }
    
    }
    
    ...
                        

    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;
    }
                        

    Change Classnames

    
    Save
    
    
    
    Edit
    
    Edit
    
    DELETE
    
    cancel
    
                        

    Use .button in HTML

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

    Use shared classes

    
    
    Save
    
    
    Sign Up!
    
    
    Edit
    
    
    Edit
    
    
    Delete
    
    
    Cancel
    
                        

    Missing States

    
    // Disabled button state - can apply to other button
    .button_disabled {
        @extend %button;
        color: #888;
        background-color: #ccc;
        cursor: default;
    
        // disabled state needs to have no shadow effects so it can apply to any button
        box-shadow: none;
        border-color: transparent;
        text-shadow: none;
        
        &:hover,
        &:active,
        &:focus {
           color: #888;
            background-color: #ccc;
        }
    }
    
    ...
    
    // Text Only buttons - for Cancel, small Edit links, etc
    .button_textonly {
        @extend %button;
        color: black;
        font-size: .8em;
        text-transform: capitalize;
        
        &:hover,
        &:active,
        &:focus {
            color: #666;
        }
    
        &.button_disabled {
            background: none;
            color: #666;
        }
    }
                        

    Create variables

    
    // Primary Buttons (Submit)
    $button_primary_background: $turquoise;
    $button_primary_color: lighten($button_primary_background,45%);
    
    // Call to Action Button (Sign Up!)
    $button_calltoaction_background: #237cbe;
    $button_calltoaction_color: $white;
    
    // Secondary Buttons (Edit)
    $button_secondary_background: #993399;
    $button_secondary_color: rgb(213,121,204);
    
    // Tertiary
    $button_tertiary_background: #e6475b;
    $button_tertiary_color: $white;
    
    // Disabled
    $button_disabled_background: $greylight;
    $button_disabled_color: $greydark;
    
    // Text Only Button
    $button_textonly_background: transparent;
    $button_textonly_color: $greydark;
                        

    Applying Variables

    
    // Primary button: for the most important action
    .button_primary {
        @extend %button;
        color: $button_primary_color;
        background-color: $button_primary_background;
        box-shadow: 0px 8px 0px 0px transparentize($button_primary_background,.75);
        border-right: 1px solid darken($button_primary_background,10%);
        border-bottom: 1px solid darken($button_primary_background,10%);
        text-shadow: 0px 1px 1px darken($button_primary_background,30%);
        
        &:hover,
        &:active,
        &:focus {
            background: darken($button_primary_background,10%);
        }
    
    }
    
    // Call to Action buttons - for large, highlighted calls to action
    .button_calltoaction {
        @extend %button;
        color: $button_calltoaction_color;
        background-color: $button_calltoaction_background;
        box-shadow: 0px 8px 0px 0px transparentize($button_calltoaction_background,.75);
        border-right: 1px solid darken($button_calltoaction_background,10%);
        border-bottom: 1px solid darken($button_calltoaction_background,10%);
        text-shadow: 0px 1px 1px darken($button_calltoaction_background,30%);
        
        &:hover,
        &:active,
        &:focus {
            background: darken($button_calltoaction_background,10%);
        }
    
    }
    
    // Secondary buttons -- for secondary actions
    .button_secondary {
        @extend %button;
        color: $button_secondary_color;
        background-color: $button_secondary_background;
        box-shadow: 0px 8px 0px 0px transparentize($button_secondary_background,.75);
        border-right: 1px solid darken($button_secondary_background,10%);
        border-bottom: 1px solid darken($button_secondary_background,10%);
        text-shadow: 0px 1px 1px darken($button_secondary_background,30%);
        
        &:hover,
        &:active,
        &:focus {
            background: darken($button_secondary_background,10%);
        }
    
    }
    
    // Disabled button state - can apply to other button
    .button_disabled {
        @extend %button;
        color: $button_disabled_color;
        background-color: $button_disabled_background;
        cursor: default;
        // disabled state needs to have no shadow effects so it can apply to any button
        box-shadow: none;
        border-color: transparent;
        text-shadow: none;
        
        &:hover,
        &:active,
        &:focus {
            background-color: $button_disabled_background;
            color: $button_disabled_color;
        }
    }
    
    // Tertiary button -- in this case, for delete, third actions
    .button_tertiary {
        @extend %button;
        color: $button_tertiary_color;
        background-color: $button_tertiary_background;
        box-shadow: 0px 8px 0px 0px transparentize($button_tertiary_background,.75);
        border-right: 1px solid darken($button_tertiary_background,10%);
        border-bottom: 1px solid darken($button_tertiary_background,10%);
        text-shadow: 0px 1px 1px darken($button_tertiary_background,30%);
        
        &:hover,
        &:active,
        &:focus {
            background: darken($button_tertiary_background,10%);
        }
    }
    
    // Text Only buttons - for Cancel, small Edit links, etc
    .button_textonly {
        @extend %button;
        color: $button_textonly_color;
        font-size: .8em;
        text-transform: capitalize;
        
        &:hover,
        &:active,
        &:focus {
            color: darken($button_textonly_color,10%);
        }
    
        &.button_disabled {
            background: none;
            color: $button_textonly_color;
        }
    }
                        

    Button Mixin

    
    @mixin button($text-color, $bg-color, $if-disabled) {
        @extend %button;
        color: $text-color;
        background-color: $bg-color;
        @if $if-disabled == false {
            box-shadow: 0px 8px 0px 0px transparentize($bg-color,.75);
            border-right: 1px solid darken($bg-color,10%);
            border-bottom: 1px solid darken($bg-color,10%);
            text-shadow: 0px 1px 1px darken($bg-color,30%);
        }
        @else if $if-disabled == true {
            // including these because button_disabled is _added_ to classes, instead of toggled, so the primary/secondary/etc styles still apply, so we have to remove them
            box-shadow: none;
            border: 0;
            text-shadow: none;
        }
        &:active, &:hover {
            background-color: darken($bg-color,10%);
        }
    }
    
    // To Include
    .button_primary {
        @include button($button_primary_color,$button_primary_background,false); // grab all the above styles
    }
                        
    
    $button_magic_color: $white;
    $button_magic_background: $green;
    
    .button_magic {
        @include button($button_magic_color,$button_magic_background);
    }
                    
    Results?
    before

    107 lines of code

    after

    91 lines of code

    But!

    That was only 5 buttons.

    add 6 buttons in CSS:

    172 lines of code

    add 6 buttons in Sass:

    91 lines of code

     

    Old CSS

    
    .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;
        }
                        

    New CSS

    
    .button_primary, .button_calltoaction, .button_secondary, .button_disabled, .button_tertiary, .button_textonly {
      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 */
    
    .button_primary {
      color: #c6eff0;
      background-color: #2ba2a6;
      box-shadow: 0px 8px 0px 0px rgba(43, 162, 166, 0.25);
      border-right: 1px solid #217a7d;
      border-bottom: 1px solid #217a7d;
      text-shadow: 0px 1px 1px #0c2b2c;
    }
    
        .button_primary:hover, .button_primary:active, .button_primary:focus {
          background: #217a7d;
        }
    
    .button_calltoaction {
      color: white;
      background-color: #237cbe;
      box-shadow: 0px 8px 0px 0px rgba(35, 124, 190, 0.25);
      border-right: 1px solid #1b6093;
      border-bottom: 1px solid #1b6093;
      text-shadow: 0px 1px 1px #0b283d;
    }
    
        .button_calltoaction:hover, .button_calltoaction:active, .button_calltoaction:focus {
          background: #1b6093;
        }
    
    .button_secondary {
      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_secondary:hover, .button_secondary:active, .button_secondary:focus {
          background: #732673;
        }
    
    .button_disabled {
      color: #474747;
      background-color: #adadad;
      cursor: default;
      box-shadow: none;
      border-color: transparent;
      text-shadow: none;
    }
    
        .button_disabled:hover, .button_disabled:active, .button_disabled:focus {
          background-color: #adadad;
          color: #474747;
        }
    
    .button_tertiary {
      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;
    }
    
        .button_tertiary:hover, .button_tertiary:active, .button_tertiary:focus {
          background: #dc1e36;
        }
    
    .button_textonly {
      color: #474747;
      font-size: .8em;
      text-transform: capitalize;
    }
        .button_textonly:hover, .button_textonly:active, .button_textonly:focus {
          color: #2e2e2e;
        }
    
        .button_textonly.button_disabled {
          background: none;
          color: #474747;
        }
    
                        

    Pre-compiled Sass

    
    
    %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 */
    
    // Primary button: for the most important action
    .button_primary {
        @extend %button;
        color: $button_primary_color;
        background-color: $button_primary_background;
        box-shadow: 0px 8px 0px 0px transparentize($button_primary_background,.75);
        border-right: 1px solid darken($button_primary_background,10%);
        border-bottom: 1px solid darken($button_primary_background,10%);
        text-shadow: 0px 1px 1px darken($button_primary_background,30%);
        
        &:hover,
        &:active,
        &:focus {
            background: darken($button_primary_background,10%);
        }
    
    }
    
    // Call to Action buttons - for large, highlighted calls to action
    .button_calltoaction {
        @extend %button;
        color: $button_calltoaction_color;
        background-color: $button_calltoaction_background;
        box-shadow: 0px 8px 0px 0px transparentize($button_calltoaction_background,.75);
        border-right: 1px solid darken($button_calltoaction_background,10%);
        border-bottom: 1px solid darken($button_calltoaction_background,10%);
        text-shadow: 0px 1px 1px darken($button_calltoaction_background,30%);
        
        &:hover,
        &:active,
        &:focus {
            background: darken($button_calltoaction_background,10%);
        }
    
    }
    
    // Secondary buttons -- for secondary actions
    .button_secondary {
        @extend %button;
        color: $button_secondary_color;
        background-color: $button_secondary_background;
        box-shadow: 0px 8px 0px 0px transparentize($button_secondary_background,.75);
        border-right: 1px solid darken($button_secondary_background,10%);
        border-bottom: 1px solid darken($button_secondary_background,10%);
        text-shadow: 0px 1px 1px darken($button_secondary_background,30%);
        
        &:hover,
        &:active,
        &:focus {
            background: darken($button_secondary_background,10%);
        }
    
    }
    
    // Disabled button state - can apply to other button
    .button_disabled {
        @extend %button;
        color: $button_disabled_color;
        background-color: $button_disabled_background;
        cursor: default;
        // disabled state needs to have no shadow effects so it can apply to any button
        box-shadow: none;
        border-color: transparent;
        text-shadow: none;
        
        &:hover,
        &:active,
        &:focus {
            background-color: $button_disabled_background;
            color: $button_disabled_color;
        }
    }
    
    // Tertiary button -- in this case, for delete, third actions
    .button_tertiary {
        @extend %button;
        color: $button_tertiary_color;
        background-color: $button_tertiary_background;
        box-shadow: 0px 8px 0px 0px transparentize($button_tertiary_background,.75);
        border-right: 1px solid darken($button_tertiary_background,10%);
        border-bottom: 1px solid darken($button_tertiary_background,10%);
        text-shadow: 0px 1px 1px darken($button_tertiary_background,30%);
        
        &:hover,
        &:active,
        &:focus {
            background: darken($button_tertiary_background,10%);
        }
    }
    
    // Text Only buttons - for Cancel, small Edit links, etc
    .button_textonly {
        @extend %button;
        color: $button_textonly_color;
        font-size: .8em;
        text-transform: capitalize;
        
        &:hover,
        &:active,
        &:focus {
            color: darken($button_textonly_color,10%);
        }
    
        &.button_disabled {
            background: none;
            color: $button_textonly_color;
        }
    }
                        

    Sass with Mixin

    
    
    // variables
    $white: #fff;
    $black: #000;
    $greydark: lighten($black,50%);
    $greylight: lighten($black,80%);
    
    $green: #9bc33f;
    $turquoise: darken(rgb(97,210,214),20%);
    $blue: #237cbe;
    $lavender: rgb(213,121,204);
    $purple: #993399;
    $coral: #e6475b;
    $tangerine: #ff753e;
    $red: rgb(249,56,49); // e6475b
    
    // Primary Buttons (Submit)
    $button_primary_background: $turquoise;
    $button_primary_color: lighten($button_primary_background,45%);
    
    // Call to Action Button (Sign Up!)
    $button_calltoaction_background: $blue;
    $button_calltoaction_color: $white;
    
    // Secondary Buttons (Edit)
    $button_secondary_background: $purple;
    $button_secondary_color: $lavender;
    
    // Tertiary
    $button_tertiary_background: $coral;
    $button_tertiary_color: $white;
    
    // Disabled - appends to and overrides any other button style!
    $button_disabled_background: $greylight;
    $button_disabled_color: $greydark;
    
    // Text Only Button (Cancel)
    $button_textonly_background: transparent;
    $button_textonly_color: $greydark;
    
    // Font Sizes
    $button_font_large: 32px;
    $button_font_medium: 24px;
    $button_font_small: 16px;
    
    %button {
        box-sizing: border-box;
        border: 0;
        border-radius: 4px;
        cursor: pointer;
        display: inline-block;
        font-family: $fontHeadline;
        font-size: $button_font_medium;
        line-height: 2.6;
        font-weight: normal;
        letter-spacing: 1px;
        margin: 0 20px 0 0;
        padding: .25em 1em;
        text-align: center;
        text-transform: uppercase;
        text-decoration: none !important;
    }
    
    @mixin button($text-color, $bg-color, $if-disabled) {
        @extend %button;
        color: $text-color;
        background-color: $bg-color;
        @if $if-disabled == false {
            box-shadow: 0px 8px 0px 0px transparentize($bg-color,.75);
            border-right: 1px solid darken($bg-color,10%);
            border-bottom: 1px solid darken($bg-color,10%);
            text-shadow: 0px 1px 1px darken($bg-color,30%);
        }
        @else if $if-disabled == true {
            // including these because button_disabled is _added_ to classes, instead of toggled, so the primary/secondary/etc styles still apply, so we have to remove them
            box-shadow: none;
            border: 0;
            text-shadow: none;
        }
        &:active, &:hover {
            background-color: darken($bg-color,10%);
        }
    }
    // to use: @include button($white,$coral);
    
    /* Button Types */
    
    .button_primary {
        @include button($button_primary_color,$button_primary_background,false); // grab all the above styles
    }
    
    .button_calltoaction {
        // @extend .button_primary; // you could also do this, if you wanted, but check the difference in the output
        @include button($button_calltoaction_color,$button_calltoaction_background,false);
        font-size: $button_font_large; // bigger font size!
    }
    
    .button_secondary {
        @include button($button_secondary_color,$button_secondary_background,false);
    }
    
    .button_tertiary {
        @include button($button_tertiary_color,$button_tertiary_background,false);
    }
    
    .button_disabled {
        @include button($button_disabled_color,$button_disabled_background,true);
        cursor: default; // remove cursor
    }
    
    .button_textonly {
        @include button($button_textonly_color,$button_textonly_background,true);
        font-size: $button_font_small; // smaller font size
        text-transform: none; // not uppercase
    }
    
                        
    Remember,

    the best color to see in a refactoring diff is red.

    Commenting?

    1. First!
    2. Comment each file to explain what is in it
    3. Comment every large section of CSS
    4. Comment individual items that need clarification
    5. Be consistent




    Comment Types













    Documentation.

    If you have documentation...

    keep it up to date when you change your code.

    if you move files, change classnames that are referenced elsewhere

    if you make or change a mixin, write it down!

    Don't have docs?

    me either.

    Start now! Next time you refactor something, comment it thoroughly, and start writing docs.











    I know it feels like
    this sometimes

    Remember:

    It's an authoring tool, don't fight it

    Start simple and iterate

    Clarity, maintainability, efficiency, DRY











    Any Questions?