Used to have fewer features (not anymore!)
Much faster than Ruby Sass
Task runner integration:
Standalone Ruby gem: sass
gem install sass
sass input.scss:output.css
Task runner integration:
Break your giant CSS file into smaller, modular partials.
Prefix your partials’ filenames with _
.
/scss
| – /base
| – _variables.scss
| – _functions.scss
| – /modules
| – _header.scss
| – _footer.scss
| – style.scss
Call your partials from your primary Sass file.
@import 'base/variables';
@import 'base/functions';
@import 'modules/header';
@import 'modules/footer';
Nesting selectors automatically creates compound selectors
.page-header {
// styles
h1 {
// styles
}
a {
// styles
}
}
.page-header {
// styles
}
.page-header h1 {
// styles
}
.page-header a {
// styles
}
Use &
to “move” the current selector context
.page-header {
.media-page & {
// styles
}
}
.media-page .page-header {
// styles
}
a {
&:hover,
&:active,
&:focus {
// styles
}
}
a:hover, a:active, a:focus {
// styles
}
Data types include string
, color
, number
, boolean
, null
$color-blue: #003366;
.blue-element {
color: $color-blue;
}
Comma- or space-separated lists
Optional parenthesis wrappers
$font-sizes: small, p, pull-quote, subheading, heading, hero;
$ff-sans: Proxima Nova, proxima-nova, Verdana, sans-serif;
$box-shadow: 1px 1px 2px rgba(0, 0, 0, .5);
Array/object: key-value pairs
$social-media: (
facebook: #3b5998,
twitter: #00aced,
google-plus: #dd4b39,
linkedin: #007bb6
);
Similar to !important
in CSS, Sass has flags to tell the compiler how overrideable a variable’s value is.
Normally, variables follow the cascade: if you reassign a variable’s value, any following uses of that variable will have the new value.
It doesn’t matter where you redefine this variable (earlier or later), a !default
will always give way to any other value assignment.
$column-count: 12 !default;
Variables within functions are scoped: use !global
to override an external variable from within a function.
@function change-size($percentage) {
$size: $size * percentage !global;
@return $size;
}
@if lightness($bg-color) > 50% {
color: $color-gray-dark;
} @else {
color: $color-cream;
}
$i: 1;
@each $size in $font-sizes {
.fs-#{$size} {
$scale: power($interval, $i);
font-size: $base * $scale;
}
$i: $i + 1;
}
@each $name, $color in $social-media {
.sm-link[href*="#{$name}"] {
background-color: $color;
}
}
to
stops before the final number
@for $i from 1 to 5 {
li:nth-child(#{$i}) {
&::before {
content: '#{$i}';
}
}
}
through
includes the final number
@for $i from 1 through 5 {
li:nth-child(#{$i}) {
&::before {
content: '#{$i}';
}
}
}
@while $line-height < $vertical-rhythm {
$line-height: $line-height * 2;
}
Manipulate input and return a value
Use them in the value side of a property declaration
darken($blue, 5%)
lighten($blue, 7.5%)
mix($blue, $shadow, 40%)
rgba($blue, .5)
$blue: #003366;
$shadow: #000102;
$blue-shadow: mix($blue, $shadow, 40%);
.button-blue {
background-color: $blue;
border: 2px solid darken($blue, 5%);
box-shadow: 0 2px 2px rgba($blue-shadow, .85);
}
.button-blue {
background-color: #003366;
border: 2px solid #00264d;
box-shadow: 0 2px 2px rgba(0, 21, 42, 0.85);
}
str-length($string)
str-index($string, $substring)
str-slice($string, $start, [$end])
percentage($number)
round($number)
random([$limit])
length($list)
nth($list, $number)
join($list1, $list2)
append($list, $item)
index($list, $value)
map-get($map, $key)
map-remove($map, $keys…)
map-keys($map)
map-values($map)
map-has-key($key)
map-merge($map1, $map1)
$social-media: (
facebook: #3b5998,
twitter: #00aced,
google-plus: #dd4b39,
linkedin: #007bb6
);
.facebook {
color: map-get($social-media, 'facebook');
}
$more-social-media: (
amazon: #ff9900,
instagram: #3f729b
);
$social-media: map-merge(
$social-media,
$more-social-media
);
$colors: ( … );
@function get-color($color) {
@if map-has-key($colors, $color) {
@return map-get($colors, $color);
}
}
@function power($x, $n) {
$return: 1;
@if $n > 0 {
@for $i from 1 through $n {
$return: $return * $x;
}
} @else if $n < 0 {
@for $i from $n to 0 {
$return: $return / $x;
}
}
@return $return;
}
Accepts zero or more arguments and outputs desired result
@mixin clearfix {
&::after {
clear: both;
content: "";
display: table;
}
}
.content {
@include clearfix;
}
@mixin button($color: $color-blue) {
background-color: $color;
border: 2px solid darken($color, 5%);
}
.button {
@include button;
}
.button.red {
@include button($color-red);
}
@mixin no-flex {
.no-flexbox & {
@content;
}
}
.flex-div {
display: flex;
@include no-flex {
display: table;
}
}
@mixin mq($breakpoint) {
@media (min-width: $breakpoint) {
@content;
}
}
body {
@include mq(900px) {
font-size: 1.2em;
}
}
Adds a selector to a previous selector’s declaration block
.card {
padding: 15px 20px;
background-color: #fff;
border: 1px solid #ccc;
box-shadow: 0 2px 2px -1px #222;
}
.image-card {
@extend .card;
border-color: #cc4;
}
.card, .image-card {
padding: 15px 20px;
background-color: #fff;
border: 1px solid #ccc;
box-shadow: 0 2px 2px -1px #222;
}
.image-card {
border-color: #cc4;
}
%selector
creates a “silent” declaration block that won’t be compiled unless a real selector extends it.
%card { … }
.image-card {
@extend %card;
border-color: #cc4;
}
.search-card {
@extend %card;
}
Stops the compiler with a fatal error
@error 'Sorry, this is what went wrong: [message]';
Allows the compiler to continue, but sends a warning message to the developer’s console
@warn 'The `border-radius()` mixin will be deprecated in version 2.0.';
Sends any message to the console: useful for debugging your own work
@debug $math-value;