Streamlining
your CSS with

@elyseholladay

Why should I use Sass?









I already
know CSS!

Have you ever heard:

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

Plan for reusability, modularity, 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.

“Sass is not a replacement for CSS, it’s more like having a CSS assistant who will help you write your code.”

Installation,
Tools, & Apps









but

I have to use the
command line!?

gem install sass

sass --watch /path/style.scss:/path/style.css

“We started building prototypes because responsive websites are fluid, and static wireframes are not. A prototype shows the site as it is meant to be seen—in a browser, on any device.”

10px speed improvement

2.5 seconds down to 0.2 seconds


  grunt.initConfig({
	watch: {
	  css: {
		files: ['Gruntfile.js', 'app/**/*.js', './app/scss/**/*.scss']
	  },
	  livereload: {
		options: { livereload: true }
	  }
	},

	connect: {
	  server: {
		options: {
		  port: 8000,
		  base: './app',
		  livereload: true
		}
	 }
	},

	open: {
	  dev : {
		path: 'http://localhost:8000'
	  }
	}
  });

  // Server Task
   grunt.registerTask('server', [
	  'connect',
	  'open',
	  'watch'
  ]);
						

  // Useful Grunt Plugins
  npm install grunt-contrib-sass       // Compile Sass to CSS with Ruby compiler
  npm install grunt-sass               // Compile Sass to CSS using node-sass/libsass
  npm install grunt-contrib-connect    // Start a web server
  npm install grunt-open               // Open server in your browser automatically
  npm install grunt-contrib-watch      // Watch for file changes
  npm install connect-livereload       // Auto-reload browser with files are changed
  npm install grunt-contrib-imagemin   // Minify PNG and JPG images
  npm install grunt-contrib-htmlmin    // Minify HTML
  npm install grunt-contrib-cssmin     // Minify compiled CSS files
  npm install grunt-autoprefixer       // Adds vendor prefixes automatically
						

Boilerplates

integralist.co.uk/Grunt-Boilerplate.html

github.com/tsevdos/Grunt-boilerplate-for-web-designers

github.com/tsevdos/Gulp-boilerplate-for-web-designers

github.com/ryanbenson/Harvest


Tips & Tricks

File & Import
Organization

  • Break out related code modules
  • Keep files small, readable
  • Comment each file with explanation, contents






➼ moourl.com/junk
➼ moourl.com/junkvid

|- sass/
|--- buttons/
|--- color/
|--- forms/
|--- layouts/
|--- modules/
|----- registration/
|------- _extends.scss
|------- _functions.scss
|------- _mixin.scss
|------- _module_registration.scss
|--- typography/
|--- ui_patterns/
|--- _buttons.scss
|--- _config.scss
|--- _forms.scss
|--- _typography.scss
|--- style.scss
						

Mina Markham's Sassy Starter

➼ moourl.com/sassystarter


  + scss/
  |
  | + base/                 # reset, typography, site-wide
  |   |-- _index.scss       # imports for all base styles
  |   |-- _base.scss        # base styles
  |   |-- _normalize.scss   # normalize v3.0.1
  |
  | + layout/               # major components, e.g., header, footer etc.
  |   |-- _index.scss       # imports for all layout styles
  |
  | + modules/              # minor components, e.g., buttons, widgets etc.
  |   |-- _index.scss       # imports for all modules
  |
  | + states/               # js-based classes, alternative states
  |   |-- _index.scss       # imports for all state styles
  |   |-- _states.scss      # state rules
  |   |-- _print.scss       # print styles
  |   |-- _touch.scss       # touch styles
  |
  | + themes/               # (optional) separate theme files
  |   |-- beccapurple.scss  # rename to appropriate theme name
  |
  | + utilities/            # non-CSS outputs (i.e. mixins, vars)
  |   |-- _index.scss       # imports for all mixins + global project vars
  |   |-- _fonts.scss       # font mixins
  |   |-- _functions.scss   # ems to rems conversion, etc.
  |   |-- _global.scss      # global variables
  |   |-- _helpers.scss     # placeholder helper classes
  |   |-- _mixins.scss      # media queries, CSS3, etc.
  |   |-- _lib.scss         # imports for third party styles
  |   |-- + lib/            # third party styles
  |       | _pesticide.scss # CSS pesticide
  |       | ...
  |
  |   + ie.scss             # IE specific Sass file
  |   + styles.scss         # primary Sass file
  |   + _shame.scss         # because hacks happen
  |
  + .htaccess               # Apache server configs
  + config.rb               # Compass configuration file
  + crossdomain.xml         # cross-domain requests
  + docs/                   # SassDoc generated documentation
  + deploy.rb               # Capistrano configuration file
  + Gruntfile.js            # Grunt configuration & tasks
  + package.json            # Grunt metadata & dependencies
						






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

Read your Output


  

  • ...

...

...

  • ...


  body {
	div.container {
	  div.content {
		div.articles {
		  & > div.post {
			div.title {
			  h1 {
				a {
				}
			  }
			}
			div.content {
			  p { ... }
			  ul {
				li { ... }
			  }
			}
			div.author {
			  a.display {
				img { ... }
			  }
			  h4 {
				a { ... }
			  }
			  p {
				a { ... }
			  }
			  ul {
				li { ... }
			  }
			}
		  }
		}
	  }
	}
  }
					

  body { ... }
  body div.content div.container { ... }
  body div.content div.container div.articles { ... }
  body div.content div.container div.articles > div.post { ... }
  body div.content div.container div.articles > div.post div.title { ... }
  body div.content div.container div.articles > div.post div.title h1 { ... }
  body div.content div.container div.articles > div.post div.title h1 a { ... }
  body div.content div.container div.articles > div.post div.content { ... }
  body div.content div.container div.articles > div.post div.content p { ... }
  body div.content div.container div.articles > div.post div.content ul { ... }
  body div.content div.container div.articles > div.post div.content ul li { ... }
  body div.content div.container div.articles > div.post div.author { ... }
  body div.content div.container div.articles > div.post div.author a.display { ... }
  body div.content div.container div.articles > div.post div.author a.display img { ... }
  body div.content div.container div.articles > div.post div.author h4 { ... }
  body div.content div.container div.articles > div.post div.author h4 a { ... }
  body div.content div.container div.articles > div.post div.author p { ... }
  body div.content div.container div.articles > div.post div.author p a { ... }
  body div.content div.container div.articles > div.post div.author ul { ... }
  body div.content div.container div.articles > div.post div.author ul li { ... }
						

don't do this.

ever.

& parent selector


  // SCSS
  .button {
	background: $red;
	&:hover { background: $blue; }
	&.is-active { background: $green; }
  }

  /* OUTPUT */
  .button { background: red;}
  .button:hover { background: blue;}
  .button.is-active { background: green;}
						

  // SCSS
  .button {
	color: $white;
	.nav & { color: $black; }
  }

  /* OUTPUT */
  .button { color: white; }
  .nav .button { color: black; }
						

  // SCSS
  $header-font: "Adelle";
  $fallback-font: "Cambria", "Georgia";

  h1, h2, h3, h4, h5, h6 {
	font-family: $header-font;
	.wf-inactive &,
	.lt-ie9 & {
	  font-family: $fallback-font;
	}
  }

  /* OUTPUT */
  h1, h2 { font-family: "Adelle"; }

  .wf-inactive h1, .lt-ie9 h1, .wf-inactive h2, .lt-ie9 h2 {
	font-family: "Cambria", "Georgia";
  }
						

State Classes


  // Buttons
  .button {
	&.is-active { background: red; color: white; }
  }

  // Nav Links
  .nav-link {
	&.is-active { color: green; }
  }

  /* OUTPUT */
  .button.is-active {
	background: red;
	color: white;
  }

  .nav-link.is-active {
	color: green;
  }

						

Namespacing Modules


  // NAV STYLES
  .nav { width: 50%; }

  .nav-item {
	display: inline-block;

	&:active,
	&:visited {
	  color: $purple;
	}
	&:hover {
	  color: $blue;
	  border-bottom: 3px solid $blue;
	}
	&.is-active {
	  color: $blue;
	  border-bottom: 3px solid $blue-dark;
	}
  }

							

  // HEADER
  .header {
	nav { float: right; }
  }

  // FOOTER
  .footer {
	background: $grey-dark;

	nav { float: left; }

	.nav-item {

	  &:active,
	  &:visited {
		color: $white;
	  }
	  &:hover {
		color: $blue-light;
		border-bottom: 3px solid $blue-light;
	  }
	  &.is-active {
		color: $blue-light;
		border-bottom: 3px solid $blue-light;
	  }
	}
  }

						

  .header nav { float: right; }

  .footer { background: #333; }
  .footer nav { float: left; }

  .footer .nav-item,
  .footer .nav-item:active,
  .footer .nav-item:visited {
	color: #fff;
  }

  .footer .nav-item:hover {
	color: #3a8bef;
	border-bottom: 3px solid #3a8bef;
  }

  .footer .nav-item.is-active {
	color: #3a8bef;
	border-bottom: 3px solid #0f59b4;
  }

							

@includes duplicates CSS

> use when you need to alter variables


@extend comma-delineates selectors

> reuse code with no extra output


						
  // SCSS

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

  // Usage
  .box { @include box; }
  .widget { @include box; }
  .sprocket { @include box; }
						
						
						
  /* OUTPUT */

  .box {
	color: #ffffff;
	background: #222222;
	padding: 10px;
  }
  .widget {
	color: #ffffff;
	background: #222222;
	padding: 10px;
  }
  .sprocket {
	color: #ffffff;
	background: #222222;
	padding: 10px;
  }
						
						
						
  // SCSS
  .box {
	color: $white;
	background: $darkgrey;
	padding: $unit/2;
  }

  .widget { @extend .box; }
  .sprocket { @extend .box; }


  /* OUTPUT */
  .box, .widget, .sprocket {
	color: #ffffff;
	background: #222222;
	padding: 10px;
  }
						
						

%placeholder

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

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

  .widget { @extend %box; }
  .sprocket { @extend %box; }


  /* OUTPUT */
  .widget, .sprocket {
	color: #ffffff;
	background: #222222;
	padding: 10px;
  }
						
						

%clearfix


  // Clearfix placeholder extend, won't compile unless used
  %clearfix {
	&:after {
	  content: "";
	  display: table;
	  clear: both;
	}
  }

  .wrap {
	width: 80%;
	// @extend %clearfix; // won't compile
  }

  .main-header {
	@extend %clearfix; // will compile
  }
						


  /* OUTPUT */
  .main-header:after {
	content: "";
	display: table;
	clear: both;
  }

  .wrap {
	width: 80%;
  }
						

Named Media Queries

						
  // SCSS
  .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;
	 }
  }
						
						
						
  // Define your breakpoint sizes; these are based on min-width MQ's
  // e.g. tiny is 0-499px
  $tiny: 0;
  $small: 500px;
  $medium: 1000px;
  $large: 1300px;
						
						
						
  // Breakpoint mixin
  @mixin breakpoint($media) {
	// if the media is 'tiny'
	@if $media == tiny {
	  // define mediaquery with variable
	  @media only screen and (min-width: $tiny) {
		// print content CSS
		@content
	  }
	}

	@else if $media == small {
	  // small and medium are 1px smaller than their previous variable
	  @media only screen and (min-width: $small - 1) {
		@content
	  }
	}

	@else if $media == medium {
	  @media only screen and (min-width: $medium - 1) {
		@content
	  }
	}

	@else if $media == large {
	  @media only screen and (min-width: $large) {
		@content
	  }
	}
  }


						
						
So you can do this magic:
						
  .page-wrap {
	@include breakpoint(tiny) { width: 95%; } // at 0 to 499px
	@include breakpoint(small) { width: 80%; } // at 500px to 999px
	@include breakpoint(medium) { width: 70%; } // at 1000px to 1299px
	@include breakpoint(large) { width: 60%; } // 1300px and up
  }
						
						

Abstracting Variables

Shared Name First


  // by shades...                       // better: by shared name
  $blue;                                $blue;
  $medium-blue;                         $blue-medium;
  $dark-blue;                           $blue-dark;
  $darkest-blue;                        $blue-darkest;
  $light-blue;                          $blue-light;
  $lightest-blue;                       $blue-pale;
						

Creating Simple Themes


// COLORS.SCSS ------------------------------------------ //

// Blues ------------------------------------------------ //
$navy: rgb(1, 46, 81); // MKS Navy
$blue-dark: rgb(23, 75, 133);
$blue-bright: rgb(35, 128, 226); // MKS Primary Blue
$blue-light: rgb(99, 159, 222);
$blue-pale: rgb(206, 226, 247);

// Secondary colors ------------------------------------- //
// use sparingly, for contrast and highlight only
$orange: rgb(255, 152, 0);
$orange-light: lighten($orange, 50%);
$orange-dark: rgb(194, 105, 1);

$green: rgb(130, 179, 0);
$green-light: lighten($green, 50%);
$green-dark: rgb(18, 120, 0);
						

// ------------------------------------------------------ //
// MAKERSQUARE DEFAULT THEME ---------------------------- //
// ------------------------------------------------------ //


// Theme colors ----------------------------------------- //

$primary-color: $navy;
$secondary-color: $blue-bright;
$tertiary-color: $grey-dark;
$highlight-color: $green;


// Text, Link, and Header colors ------------------------ //

$text-color: $black;
$text-color-sub: $grey;
$text-color-reverse: $off-white;

$header-color-primary: $navy;
$header-color-secondary: $blue-bright;
$header-color-tertiary: $blue-dark;

$link-color: $secondary-color;
$link-color-hover: $highlight-color;


// Background colors ------------------------------------ //

$background-color-default: $white;
$background-color-light: $off-white;
$background-color-dark: $navy;
$background-color-standout: $blue-bright;


// Button colors ---------------------------------------- //

$button-bg: $green;
$button-color: $white;
$button-bg-hover: $green-dark;
$button-color-hover: $white;
						

// ------------------------------------------------------ //
// Configuration File Import ---------------------------- //
// ------------------------------------------------------ //

// Import the configuration file that sets default variables
@import "config/config";

// Import helpers, vars, and mixins
@import "config/helpers";
@import "config/colors";

// Choose theme
@import "config/theme-mks-default";
// @import "config/theme-mks-dark";
// @import "config/theme-mks-light";

// Import webfonts, typography
@import "config/webfonts";
@import "config/typography";


						



Gutter Color for Sublime

moourl.com/sublgc

// Single-line JS-style comments don't need a closing tag,
// and they are hidden from your compiled output! This is
// great for inline documentation about the styles themselves
// and how they should be used, mentioning hacks, or TODOs.

/* CSS style comments can be multi-line with a closing tag
   and they appear in your compiled output. This is great for
   section documentation and headers/dividers! */
					












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 write something, comment it thoroughly, and start writing docs.

Testing Mixins

moourl.com/sasstest

  @mixin show-breakpoints {
	@include breakpoint(tiny) {
	  border-top: 30px solid blue;

	  &:before {
		// this variable sets the max of the range, e.g. 0 - 500px
		$tiny-defined: $small - 1;
		content: "#{$tiny}px to #{$tiny-defined}";
		color: white;
		position: absolute;
		top: 5px;
		left: 5px;
	  }
  }

	@include breakpoint(small) {
	  border-top: 30px solid purple;
	  &:before {
		$small-defined: $medium - 1;
		content: "#{$small} to #{$small-defined}";
		color: white;
		position: absolute;
		top: 5px;
		left: 5px;
	  }
  }

	@include breakpoint(medium) {
	  border-top: 30px solid red;
	  &:before {
		$medium-defined: $large - 1;
		content: "#{$medium} to #{$medium-defined}";
		color: white;
		position: absolute;
		top: 5px;
		left: 5px;
	  }
  }

	@include breakpoint(large) {
	  border-top: 30px solid orange;
	  &:before {
		content: "#{$large} to max";
		color: white;
		position: absolute;
		top: 5px;
		left: 5px;
	  }
  }
}

						

  // Usage: Include this mixin in the body while testing
  body {
	@include show-breakpoints;
	margin: 0;
}
					

  $test-mode: on;

  body {
	@if $test-mode == on {
	  @include show-breakpoints;
	}
	margin: 0
  }
						

“but there's a Grunt plugin that...”

Sass is an authoring tool.

Make it work for you.









Questions?