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.
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;
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?
//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:
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:
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
More in-depth:
donuts.js
By Cory Brown
donuts.js
SASS in depth and in practice
- 2,182