donuts.js

SASS
In Depth & In Practice

Syntactically

Awesome

Style

Sheets

CSS Preprocessors

  • Consistent styles
  • Reusable components
  • Encapsulated modules
  • Faster Development
  • Requires a build step
  • Another thing to learn

SASS vs. Stylus

FIGHT!

  • Thriving community
  • Most used CSS preprocessor
  • Most feature rich
  • Syntactically similar to Stylus and CSS
  • Many open bugs
  • Dwindling community
  • Uncertain future

SASS vs. SCSS

FIGHT!

SASS

SCSS

$box-model: border-box

html

    box-sizing: $box-model

*, *::before, *::after

    box-sizing: inherit
$box-model: border-box;

html {

    box-sizing: $box-model;
}

*, *::before, *::after {

    box-sizing: inherit;

}

Variables

Variables begin with $.

 

assignment is done like a CSS rule:

$variable-name: value;

 

A variable can be assigned any SASS type: string, number, bool, color, list, map or null.

 

Scope

SASS variables can have one of two scopes: selector level and global.

 

A variable defined within a selector is scoped to that selector only if there is not an existing global variable with the same name. If there is, the local assignment overrides the global value.

More in-depth:

Sass Variable "Scope" (gist)

 

Be BEM-ish 

When naming variables, they should use the type as the block and the identifier as modifier.

 

Be descriptive in your names so you know what they mean.

 

Sometimes adding an additional layer of abstraction gives greater flexibility.



//Colors

//Named colors
$color--rebecca-purple: #663399;
$color--bad-green: #hsl(74, 64.2512%, 59.4118%);


//Color Purposes
$color--primary: $color--rebecca-purple;
$color--secondary: $color--bad-green;

More in-depth:

Sass Color Variables

 

Organization

Organization

Any variable that is not component specific should be in a vars.scss

 

Group your variables.

colors

typography

media queries

etc.

//vars.scss

//Colors

    //Named colors
$color--rebecca-purple: #663399;
$color--bad-green: #hsl(74, 64.2512%, 59.4118%);

$color--black: hsl(0, 10%, 14%);
$color--white: hsl(0, 20%, 98%);


    //Color Purposes
$color--primary: $color--rebecca-purple;
$color--secondary: $color--bad-green;


//Typography
$font--body: Helvetica, sans;
$font--title: Josephine, serif;

$text-size--title: 2.4em; 


//Media Queries
$mq--small: only screen and (min-width: 42em);
$mq--med: only screen and (min-width: 47.5em);
$mq--large: only screen and (min-width: 56.25em);

@import

import your component files into your main.scss

 

Why not sass-globbing?

node-sass does not support sass-globbing. The ruby requires a specific unmerged pull request.

//main.scss

@import 'vars';

@import 'functions';
@import 'mixins';

@import 'componentA';
@import 'componentB';

Selector Nesting

SASS lets you nest selectors

(those with weak constitutions may want to avert their eyes)

html {
  body {    
    main {
      article {
        &::before {
          content: "Sweet fancy musturd,
                    WHY! My eyes! 
                    It hurts my EYES!";
        }

        &::after {
          content: "Several kittens &
                    puppies died as a
                    result of writing
                    this example.";
        }
      }
    }
  }
}
html body main article::before {
  content: "Sweet fancy mustard,
            WHY! My eyes! 
            It hurts my EYES!";
}

html body main article::after {
  content: "Many kittens and 
            puppies died as a
            result of writing 
            this example.";
}

>>

Depth

I told you we'd be going in depth ;)

The flatter the better, but as a rule, keep your nesting to a strict maximum depth of 3.

//BAD
.dialog {
    max-width: 80vw;
    max-height: 80vh;
    
    .dialog__backdrop {
        display: none;

        &.dialog__backdrop--open {
            display: block;
            background-color: hsla(0, 0%, 0%, .2);
            position: fixed;
            width: 100vw;
            height: 100vh;
        }
    }
}
//Processed BAD
.dialog {
    max-width: 80vw;
    max-height: 80vh;
}    
.dialog__backdrop {
    display: none;
}

.dialog__backdrop.dialog__backdrop--open {
    display: block;
    background-color: hsla(0, 0%, 0%, .2);
    position: fixed;
    width: 100vw;
    height: 100vh;
}

BEM + nesting = :(

Typically this means if you're writing BEM, you won't need any nesting at all.

//LESS BAD
$max-dialog-size: 80;
.dialog {
  max-width: $max-dialog-size * 1vw;
  max-width: $max-dialog-size * 1vh;  
}
.dialog__backdrop {
    display: none;
}
.dialog__backdrop--open {
    display: block;
    background-color: hsla(0, 0%, 0%, .2);
    position: fixed;
    width: 100vw;
    height: 100vh;
}
//Processed LESS BAD
.dialog {
    max-width: 80vw;
    max-height: 80vh;
}
.dialog__backdrop {
    display: none;
}
.dialog__backdrop--open {
    display: block;
    background-color: hsla(0, 0%, 0%, .2);
    position: fixed;
    width: 100vw;
    height: 100vh;
}

& then there was &.

The ampersand ("&") SASS operator gives us a way to nest in an intuitive way that resolves to really flat CSS selectors.

 

"&" acts as a representation of the fully expanded parent selector string. 

//HOTNESS
$max-dialog-size: 80;
.dialog {
  max-width: $max-dialog-size * 1vw;
  max-width: $max-dialog-size * 1vh; 
    
  &__backdrop {
    display: none;

    &--open {
      display: block;
      background-color: hsla(0, 0%, 0%, .2);
      position: fixed;
      width: 100vw;
      height: 100vh;
    }
  }
}
//Processed HOTNESS
.dialog {
  max-width: 80vw;
  max-height: 80vh;
} 
.dialog__backdrop {
  display: none;
}
.dialog__backdrop--open {
   display: block;
   background-color: hsla(0, 0%, 0%, .2);
   position: fixed;
   width: 100vw;
   height: 100vh;
}

Interpolate This

The Hash Stash

#{expression}

Interpolation lets one use the value of a variable or expression in places other than as a CSS property value.

 

Here we're using it in three unique places to build our our media queries.

 

Can you find them all?

 

gist

//Media Queries
$mq-size--small: 42em;
$mq-size--med:   47.5em;
$mq-size--large: 56.25em;

$mq-dir: "min-width";

$mq--small: "(#{$mq-dir}: #{$mq-size--small})";
$mq--med:   "(#{$mq-dir}: #{$mq-size--med})";
$mq--large: "(#{$mq-dir}: #{$mq-size--large})";


@media #{$mq--med} {
    .sidebar {
        width: 33.3%;
    }
}
@media (min-width: 47.5em) {
  .sidebar {
    width: 33.3%;
  }
}

Maths

Proportional Sizes

Sass supports + - * / and %. 

 

As with any applied math, the gotcha is with units. The resultant unit must be a valid CSS unit.

$size: 10em;
$margin: 2em;

$size--plus: $size + 2em; // 12em

$size--double: $size * 2; // 20em

$size--square: $size * $size; // WAT? em^2 is not valid

$size--margin-adjust: 100% - $margin; // Works with CSS calc(), not in a preprocessor

$size--half: $size * 50%; // Nope. SASS doesn't know how to multiply em * %
$size--half: $size * .5;  // But this works: 5em

Grid Demo

(assuming we're not using flexbox -- which we should be)

Let SASS do the math for space between grid items.

Extending Selectors

@extend

extend the rules of a selector into others. 

%placeholders

Placeholders work the same as regular selectors except they are intended to only be extended.

 

They render nothing themselves, only when they are extended.

Mixins

@mixin

The @mixin directive defines a mixin. 

Mixins are like function in that they can take arguments. Arguments can also have default values.

@include

The @include directive "invokes" a mixin.

Functions

Color Functions

rgb(a), hsl(a)

red, green, blue

hue, saturation, lightness

adjust-hue

lighten, darken

saturate, desaturate,

complement, invert,

opacify, transparentize

mix

ie-hex-str

String Functions

quote/unquote, str-length, str-insert, str-index, str-slice, to-upper-case, to-lower-case

Number Functions

percentage, round, ceil, floor, abs, min, max, random

List Functions

length, nth, set-nth, join, append, zip, index, list-seperator

Map Functions

map-get, map-merge, map-remove, map-keys, map-value, map-has-key, keywords

Selector Functions

selector-nest

selector-append

selector-extend

selector-replace

selector-unify

is-superselector

simple-selector

selector-parse

Introspection Functions

feature-exists

variable-exists

global-variable-exists

function-exists

mixin-exists

inspect

type-of

unit

unitless

comparable

call

More in-depth:

SASS Documentation

Custom Functions

@function directive

Accepts arguments

Returns a single value

@return  directive

@if for conditionals

@if else also

@for for loops

@each for iterables

@warn to message to std out

 

@function social-selector-gen($social) {
  $base: '.social--';
  
  @return str-insert($base, $social, str-length($base) + 1);
}

Data Structures

  • Lists
  • Maps

Lists

A list in SASS is a collection of values.

 

SASS is very permissive in type definitions so care should be taken.

 

SASS will type coherce so strings, numbers, etc. can be quoted or not. Even lists need not be braced.

 

List items can be comma separated or  space* separated.

*provided there are no spaces in the value.

More in-depth:

Understanding Sass Lists

Maps

A Map in SASS is simply a list of key-value pairs.

 

Both keys & values can be of any SASS type, INCLUDING OTHER MAPS!

Maps

Get values using map-get

 

Set map entries with map-merge

donuts.js

By Cory Brown

donuts.js

SASS in depth and in practice

  • 2,072