Wifi Network:
Password:
alexhasnicehair.com | @alexjvasquez | alexjvas@gmail.com
What is your name?
What is your 140 character bio?
What is your fave dev tool / plugin / blog / etc.?
Client: Kittens "R" Us
Description: A local adoption listing website for kitties in need of homes.
Framework: HTML5 & CSS3
Slides + Project: http://gdila.github.io/sass-shop
exercises
directory in your text editor of choice.exercises/kittens
directory is where we'll be doing our work.exercises/kittens
directory via the command line.sass -v
to verify that Sass is installed.exercises/kittens/index.html
in your browser.What is Sass?
Syntactically Awesome StyleSheets
Like CSS, but AWESOME
.header {
background-color: #531946;
border-radius: 5px;
padding: 5px 20px;
}
.header a {
color: #fff;
}
.header a:hover {
color: #095169;
}
.footer {
background-color: #30162B;
color: #fff;
border-radius: 5px;
padding: 5px 20px;
}
.footer a {
color: #095169;
}
.footer a:hover {
color: #fff;
}
.feature a {
background-color: #30162B;
color: #fff;
border-radius: 5px;
padding: 5px 20px;
}
.feature a:hover {
color: #531946;
}
.content {
background-color: #fff;
color: #222;
border-radius: 5px;
padding: 5px 20px;
}
Don't Repeat Yourself
HEX Colors
Border Radius + Padding
Selectors
.header {
background-color: #531946;
}
.header a {
color: #fff;
}
.header a:hover {
color: #095169;
}
.footer {
background-color: #30162B;
color: #fff;
}
.footer a {
color: #095169;
}
.footer a:hover {
color: #fff;
}
.feature a {
background-color: #30162B;
color: #fff;
}
.feature a:hover {
color: #531946;
}
.content {
background-color: #fff;
color: #222;
}
$white: #ffffff;
$black: #222222; // NOT! ;)
$eggplant: #531946;
$eggplantDark: #30162B;
$teal: #095169;
.header {
background-color: $eggplant;
}
.header a {
color: $white;
}
.header a:hover {
color: $teal;
}
.footer {
background-color: $eggplantDark;
color: $white;
}
.footer a {
color: $teal;
}
.footer a:hover {
color: $white;
}
.feature a {
background-color: $eggplantDark;
color: $white;
}
.feature a:hover {
color: $eggplant;
}
.content {
background-color: $white;
color: $black;
}
.header {
border-radius: 5px;
padding: 5px 20px;
/* ... */
}
.footer {
border-radius: 5px;
padding: 5px 20px;
/* ... */
}
.feature a {
border-radius: 5px;
padding: 5px 20px;
/* ... */
}
.content {
border-radius: 5px;
padding: 5px 20px;
/* ... */
}
@mixin rounded-box {
border-radius: 5px;
padding: 5px 20px;
}
.header {
@include rounded-box;
// ...
}
.footer {
@include rounded-box;
// ...
}
.feature a {
@include rounded-box;
// ...
}
.content {
@include rounded-box;
// ...
}
.header {
/* ... */
}
.header a {
/* ... */
}
.header a:hover {
/* ... */
}
.footer {
/* ... */
}
.footer a {
/* ... */
}
.footer a:hover {
/* ... */
}
.feature a {
/* ... */
}
.feature a:hover {
/* ... */
}
.content {
/* ... */
}
.header {
// ...
a {
// ...
&:hover {
// ...
}
}
}
.footer {
// ...
a {
// ...
&:hover {
// ...
}
}
}
.feature a {
// ...
&:hover {
// ...
}
}
.content {
// ...
}
.header {
background-color: #531946;
border-radius: 5px;
padding: 5px 20px;
}
.header a {
color: #fff;
}
.header a:hover {
color: #095169;
}
.footer {
background-color: #30162B;
color: #fff;
border-radius: 5px;
padding: 5px 20px;
}
.footer a {
color: #095169;
}
.footer a:hover {
color: #fff;
}
.feature a {
background-color: #30162B;
color: #fff;
border-radius: 5px;
padding: 5px 20px;
}
.feature a:hover {
color: #531946;
}
.content {
background-color: #fff;
color: #222;
border-radius: 5px;
padding: 5px 20px;
}
$white: #ffffff;
$grey: #222222;
$eggplant: #531946;
$eggplantDark: #30162B;
$teal: #095169;
// Mixins
@mixin rounded-box {
border-radius: 5px;
padding: 5px 20px;
}
// Site Stylez
.header {
@include rounded-box;
background-color: $eggplant;
a {
color: $white;
&:hover { color: $teal; }
}
}
.footer {
@include rounded-box;
background-color: $eggplantDark;
color: $white;
a {
color: $teal;
&:hover { color: $white; }
}
}
.feature a {
@include rounded-box;
background-color: $eggplantDark;
color: $white;
&:hover { color: $eggplant; }
}
.content {
@include rounded-box;
background-color: $white;
color: $grey;
}
Sass is an extension of CSS that adds power and elegance to the basic language. It allows you to use variables, nested rules, mixins, inline imports, and more, all with a fully CSS-compatible syntax. Sass helps keep large stylesheets well-organized, and get small stylesheets up and running quickly...
~ Sass DocumentationCSS Extension
SassScript Language
CSS Preprocessor
SassScript Interpreter
Sass CSS
.scss (Sassy CSS)
Default syntax; Valid CSS == Valid SCSS
$black: #000;
$white: #fff;
.this {
color: $black;
background: $white;
}
.that {
color: $white;
background: $black;
}
.this, .that {
display: block;
&:hover {
border: 1px solid;
}
}
.sass (Indented)
Original syntax, still supported; Haml-esque
$black: #000
$white: #fff
.this
color: $black
background: $white
.that
color: $white
background: $black
.this, .that
display: block
&:hover
border: 1px solid
The Sass Workflow
$ gem install sass
(aka Compiling Sass)
Sass CSS
$ sass source.scss output.css
$ sass source_dir output_dir
$ sass sass/screen.scss css/screen.css
$ sass --watch source.scss:output.css
$ sass source.scss output.css
source.scss
to output.css
. Gotchya: will fail if output directory doesn't exist.$ sass --update source.scss:output.css
source.scss
to output.css
. Will create directory if it doesn't exist.$ sass --watch source.scss:output.css
source.scss
for changes. Will create directory if it doesn't exist.$ sass --style expanded source.scss:output.css
Sourcemaps tell browsers where the Sass that generated the CSS is.
You must enable sourcemaps in the browser to use this feature.
index.html
in the broswercss/application.css
to sass/application.scss
mkdir sass && cp css/application.css sass/application.scss
application.css
rm css/application.css
sass --watch --style expanded sass/application.scss:css/application.css
Enjoy some snacks!
courtesy of
Sass Basics
HTML has a clear hierarchy - elements are nested.
We can apply the same concept in Sass.
<nav class="navigation">
<ul>
<li><a href="#">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
.navigation {
float: right;
li {
display: inline-block;
list-style-type: none;
margin-left: 1.5em;
}
a {
display: block;
text-decoration: none;
}
}
.navigation {
float: right;
li {
display: inline-block;
list-style-type: none;
margin-left: 1.5em;
}
a {
display: block;
text-decoration: none;
}
}
.navigation {
float: right;
}
.navigation li {
display: inline-block;
list-style-type: none;
margin-left: 1.5em;
}
.navigation a {
display: block;
text-decoration: none;
}
Sass gives us organization.
Sass reduces repetition.
Mirroring HTML nesting is SUPER easy.
Overly verbose and overly specific.
Rule of thumb or "the Inception Rule": no more than 3 levels deep.
body {
.header {
.navigation {
ul {
li {
a {
// ...
}
}
}
}
}
}
body .header .navigation ul li a {
// ...
}
Sass allows us to reference the current parent selector(s) via the ampersand character:
a {
color: #beedee;
&:hover {
color: #cbbebb;
}
&.btn {
background: #deede6;
}
.btn {
display: block;
}
}
a {
color: #beedee;
}
a:hover {
color: #cbbebb;
}
a.btn {
background: #deede6;
}
a .btn {
display: block;
}
The &
selector can follow other selectors. This will override the parent element's (&
) styles when it exists within the preceding selector.
a {
.footer & {
text-decoration: none;
span {
opacity: .5;
}
}
span {
.navigation & {
display: block;
}
}
}
.footer a {
text-decoration: none;
}
.footer a span {
opacity: .5;
}
.navigation a span {
display: block;
}
The &
selector can also be combined with strings.
PERFECT for BEM, child elements, states, and modifications.
.module {
&__child {
margin-bottom: 1em;
}
&--modifier {
display: inline;
}
}
.module__child {
margin-bottom: 1em;
}
.module--modifier {
display: inline;
}
The &
contents can also be accessed as a string/list and used in SassScript.
.thing.thang {
.thung {
content: &;
content: &--mod;
content: selector-append(&, '--mod');
&--mod { content: 'here'; }
}
}
.thing.thang .thung {
content: .thing.thang .thung;
content: .thing.thang .thung --mod;
content: .thing.thang .thung--mod;
}
.thing.thang .thung--mod {
content: 'here';
}
There are a handful of additional selector methods.
You can also nest namespaced properties
a {
border: {
color: #deedee;
style: solid;
width: 2px;
}
}
a {
border-color: #deedee;
border-style: solid;
border-width: 2px;
}
NEST ALL THE THINGS!!
BUT Don't nest too deeply!
And just kidding...
You don't need to nest everything.
P.S. Don't Forget
The parent &
selector.
Oh! And feel free to rename things!
// good
.navigation {
li {//...}
a {
//...
&:hover { //... }
}
}
// bad
body {
.header {
.navigation {
ul {
li {
a {
// ...
}
}
}
}
}
}
sass --watch --style expanded sass/application.scss:css/application.css
Variables
Variables are defined à la CSS prop/val pairs: $property: value;
Variables are then referenced by their $
-prefixed names.
$grey: rgba(0,0,0,.5);
$teal: #095169;
$default-padding: 1em;
a {
color: $teal;
padding: $default-padding;
&:hover {
color: $grey;
background: $teal;
}
}
p { padding-bottom: $default-padding; }
a {
color: #095169;
padding: 1em;
}
a:hover {
color: rgba(0,0,0,.5);
background: #095169;
}
p {
padding-bottom: 1em;
}
$base-padding: 10px;
$line-height: 1.5;
$base-font: Verdana;
$content: "Loading...";
$feature-color: purple;
$feature-background: rgba(0, 255, 0, 0.5);
$base-margin: 20px 0 30px 10px;
$base-font: "Trebuchet MS", "Verdana", "Arial";
$bordered: true;
$shadowed: false;
$secondary: null;
$map: (key1: value1, key2: value2, key3: value3);
You can redeclare variables to override previous values
$border-width: 2px;
$border-width: 4px;
a {
border: $border-width solid $teal;
}
a {
border: 4px solid #095169;
}
The !default flag
Means: set if it does not already have a value
$border-width: 2px;
$border-width: 4px !default;
a {
border: $border-width solid $teal;
}
a {
border: 2px solid #095169;
}
Variables are only available within the level of nested selectors where they're defined.
If they're defined outside of any nested selectors, they're available everywhere.
$border-width: 4px; // Global
a {
$color: orange; // Local
border: $border-width solid $color;
}
p {
border: $border-width solid $color;
// ERROR!! Undefined variable "$color"
}
Changes to "global" variables within a selector are local to that selector.
UNLESS defined with the !global
flag, in which case the change is set globally.
$border-width: 4px;
$border-radius: 4px;
a {
$border-width: 2px !global;
$border-radius: 8px;
border-width: $border-width; //=> 2px
border-radius: $border-radius; //=> 8px
}
p {
border-width: $border-width; //=> 2px
border-radius: $border-radius; //=> 4px
}
Variables can be injected into selectors,
property names and values, and strings via #{$variable}
$side: left;
.box-#{$side} {
border-#{$side}: 1px solid #ccc;
&:before {
content: "It's on the #{$side} side";
}
}
.box-left {
border-left: 1px solid #ccc;
}
.box-left:before {
content: "It's on the left side";
}
$white: #ffffff;
$black: #222222;
$eggplant: #531946;
$eggplantDark: #30162B;
$teal: #095169;
.header {
background-color: $eggplant;
}
.header a {
color: $white;
}
.header a:hover {
color: $teal;
}
.footer {
background-color: $eggplant-dark;
color: $white;
}
.footer a {
color: $teal;
}
.footer a:hover {
color: $white;
}
.feature a {
background-color: $eggplant-dark;
color: $white;
}
.feature a:hover {
color: $eggplant;
}
.content {
background-color: $white;
color: $black;
}
// Descriptive Colors
$white: #ffffff;
$grey: #222222;
$eggplant: #531946;
$eggplantDark: #30162B;
$teal: #095169;
// Functional Colors
$header--background: $eggplant;
$header__logo--color: $white;
$header__logo--color--hover: $teal;
$footer--background: $eggplant--dark;
$footer--color: $white;
$footer__nav--color: $teal;
$footer__nav--color--hover: $white;
$content--background: $white;
$content--color: $grey;
$feature__link--background: $eggplant--dark;
$feature__link--color: $white;
$feature__link--color--hover: $eggplant;
.header {
background-color: $header--background;
}
.header a {
color: $header__logo--color;
}
.header a:hover {
color: $header__logo--color--hover;
}
.footer {
background-color: $footer--background;
color: $footer--color;
}
.footer a {
color: $footer__nav--color;
}
.footer a:hover {
color: $footer__nav--color--hover;
}
.feature a {
background-color: $feature__link--background;
color: $feature__link--color;
}
.feature a:hover {
color: $eggplant;
}
.content {
background-color: $content--background;
color: $content--color;
}
DRY Up Your Styles
Add Variables
Repetitious values.
"Arbitrary" values.
sass --watch --style expanded sass/application.scss:css/application.css
Imports & Organization
CSS @import
has always meant an extra file download.
Sass modifies @import
to instead include the resource during compilation, rather than on the client side.
Organization OMGZZ!
@import "vars";
@import "compass";
@import "fontawesome";
@import "utilities";
@import "grid";
@import "base";
@import "modules/all";
@import "pages/all";
@import
takes a path to a Sass resource, the file extension is optional.
These @import
s look for resources within the main Sass file's parent directory.
@import "vars";
@import "compass";
@import "fontawesome";
@import "utilities";
@import "grid";
@import "base";
@import "modules/all";
@import "pages/all";
@import "vars";
@import "lib/compass";
@import "lib/fontawesome";
@import "utilities";
@import "grid";
@import "base";
$ sass sass/screen.scss css/screen.css
$ sass sass/ css/
$ sass sass/ css/
.css
on their own, they must be @import
ed_vars.scss
is a separate sheet for all your variables_vars.scss
is added first so that it's variables can override later !default
ed ones
// Variables
@import "vars";
// Libraries
@import "lib/compass";
@import "lib/fontawesome";
// Base Styles, Utility Classes, etc.
@import "base/all";
// Individual Components
@import "modules/all";
// Page Styles
@import "pages/all";
application.scss
to more modular / organized locations.application.scss
to @import
your new partials.sass --watch --style expanded sass/application.scss:css/application.css
Enjoy some snacks!
courtesy of
Sass Intro
Workflow
Nesting
Variables
Imports & Organization
Questions?
#SassShop
Sass Intro
Workflow
Nesting
Variables
Imports & Organization
Questions?
exercises
directory in your text editor of choice.exercises/kittens
directory is where we'll be doing our work.exercises/kittens
directory via the command line.cd
to navigate the file structure.sass --watch --style expanded sass/application.scss:css/application.css
exercises/kittens/index.html
in your browser.exercises
director in your file browser.kittens
, create a new folder called "day1".kittens
into the new day1
folder.index.html
, sass/
, and css/
.kittens-3-imports
into the kittens
folder.Advanced Sass
Variables let you reuse single values.
Mixins let you reuse blocks of styles.
.header {
border-radius: 5px;
padding: 5px 20px;
}
.footer {
border-radius: 5px;
padding: 5px 20px;
}
.feature a {
border-radius: 5px;
padding: 5px 20px;
}
.content {
border-radius: 5px;
padding: 5px 20px;
}
@mixin rounded-box {
border-radius: 5px;
padding: 5px 20px;
}
.header {
@include rounded-box;
}
.footer {
@include rounded-box;
}
.feature a {
@include rounded-box;
}
.content {
@include rounded-box;
}
@mixin rounded-box {
border-radius: 5px;
padding: 5px 20px;
}
.header {
@include rounded-box;
color: $header-color;
// ...
}
.footer {
@include rounded-box;
// ...
}
.feature a {
@include rounded-box;
// ...
}
.content {
@include rounded-box;
// ...
}
@mixin
directive.@include
directive.
@mixin rounded-box {
border-radius: 5px;
padding: 5px 20px;
}
.header {
@include rounded-box;
color: $header-color;
// ...
}
.footer {
@include rounded-box;
// ...
}
.feature a {
@include rounded-box;
// ...
}
.content {
@include rounded-box;
// ...
}
.header {
border-radius: 5px;
padding: 5px 20px;
color: $header-color;
/* ... */
}
.footer {
border-radius: 5px;
padding: 5px 20px;
/* ... */
}
.feature a {
border-radius: 5px;
padding: 5px 20px;
/* ... */
}
.content {
border-radius: 5px;
padding: 5px 20px;
/* ... */
}
.header {
border-radius: 5px;
padding: 5px 20px;
/* ... */
}
.footer {
border-radius: 5px;
padding: 5px 20px;
/* ... */
}
.feature a {
border-radius: 5px;
padding: 5px 20px;
/* ... */
}
.content {
border-radius: 5px;
padding: 5px 20px;
/* ... */
}
.header,
.footer,
.feature a,
.content {
border-radius: 5px;
padding: 5px 20px;
}
Vendor Prefixes?!
@mixin rounded-corners {
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
}
.header {
@include rounded-corners;
// ...
}
.footer {
@include rounded-corners;
// ...
}
But... we're still really just copying the same properties and values all over the place.
Mixins are great for repeated blocks of styles
where the values differ from case to case.
@mixin rounded-corners($radius) {
-webkit-border-radius: $radius;
-moz-border-radius: $radius;
border-radius: $radius;
}
.header {
@include rounded-corners(5px);
// ...
}
.footer {
@include rounded-corners(10px);
// ...
}
.header {
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
// ...
}
.footer {
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
// ...
}
You can also set an argument's default value,
making it optional to pass one in.
@mixin rounded-corners($radius: 5px) {
-webkit-border-radius: $radius;
-moz-border-radius: $radius;
border-radius: $radius;
}
.header {
@include rounded-corners;
// ...
}
.footer {
@include rounded-corners(10px);
// ...
}
.header {
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
// ...
}
.footer {
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
// ...
}
Comma separated, order matters.
@mixin rounded-box($radius, $padding) {
border-radius: $radius;
padding: $padding;
}
.header {
@include rounded-box(5px, 20px);
color: $header-color;
// ...
}
.footer {
@include rounded-box(20px, 40px);
// ...
}
.header {
border-radius: 5px;
padding: 20px;
// ...
}
.footer {
border-radius: 20px;
padding: 40px;
// ...
}
Optional arguments (ones with defaults) must be last in the set.
@mixin rounded-box($radius: 5px, $padding: 20px) {
border-radius: $radius;
padding: $padding;
}
@mixin content($color, $font-size: 12px) {
color: $color;
font-size: $font-size;
}
.header {
@include rounded-box;
@include content(#aaa, 24px);
// ...
}
.footer {
@include rounded-box(20px, 40px);
@include content(#ccc);
// ...
}
.header {
border-radius: 5px;
padding: 20px;
color: #aaa;
font-size: 24px;
// ...
}
.footer {
border-radius: 20px;
padding: 40px;
color: #ccc;
font-size: 12px;
// ...
}
Order doesn't matter!
@mixin rounded-box($radius: 5px, $padding: 20px) {
border-radius: $radius;
padding: $padding;
}
.header {
@include rounded-box($padding: 10px, $radius: 10px);
}
.footer {
@include rounded-box($padding: 40px);
}
@mixin toolbar-border($side, $width: 2px) {
border-#{$side}: $width solid $toolbar-border;
}
.sidebar--left {
@include toolbar-border("right");
}
.sidebar--right {
@include toolbar-border("left");
}
.sidebar--left {
border-right: 2px solid #213213;
}
.sidebar--right {
border-left: 2px solid #213213;
}
@import
it in your main .scss
file.border-radius
@mixin
with a required radius argument. Include common vendor prefixes.@mixin
for scaled transformations.transform: scale(1.1);
@include
these mixins wherever they can be applied.Feel free to experiment and play around!
sass --watch --style expanded sass/application.scss:css/application.css
Inheritance
.header {
border-radius: 5px;
padding: 5px 20px;
/* ... */
}
.footer {
border-radius: 5px;
padding: 5px 20px;
/* ... */
}
.feature a {
border-radius: 5px;
padding: 5px 20px;
/* ... */
}
.content {
border-radius: 5px;
padding: 5px 20px;
/* ... */
}
.header,
.footer,
.feature a,
.content {
border-radius: 5px;
padding: 5px 20px;
}
@extend
let's us group selectors together!
.message {
border: 2px solid;
margin-bottom: 1em;
padding: 1em;
text-align: center;
}
.message-alert {
@extend .message;
@include message-colors($yellow);
}
.message-error {
@extend .message;
@include message-colors($red);
}
.message-notice {
@extend .message;
@include message-colors($green);
}
.message,
.message-alert,
.message-error,
.message-notice {
border: 2px solid;
margin-bottom: 1em;
padding: 1em;
text-align: center;
}
.message-alert {
border-color: goldenrod;
color: goldenrod;
}
.message-error {
border-color: darkred;
color: darkred;
}
.message-notice {
border-color: green;
color: green;
}
.message {
border: 2px solid;
margin-bottom: 1em;
padding: 1em;
text-align: center;
}
.message-alert {
@extend .message;
@include message-colors($yellow);
}
.message-error {
@extend .message;
@include message-colors($red);
}
.message-notice {
@extend .message;
@include message-colors($green);
}
@extend
directive.If we don't need to apply both .message
AND .message-alert
to an element, then why do we even need .message
anymore?!
.message {
border: 2px solid;
margin-bottom: 1em;
padding: 1em;
text-align: center;
}
.message-alert {
@extend .message;
@include message-colors($yellow);
}
.message-error {
@extend .message;
@include message-colors($red);
}
.message-notice {
@extend .message;
@include message-colors($green);
}
.message,
.message-alert,
.message-error,
.message-notice {
border: 2px solid;
margin-bottom: 1em;
padding: 1em;
text-align: center;
}
.message-alert {
border-color: goldenrod;
color: goldenrod;
}
.message-error {
border-color: darkred;
color: darkred;
}
.message-notice {
border-color: green;
color: green;
}
Placeholders are a special type of selector denoted by a %
. They can be @extend
ed but will never compile to the CSS on their own.
%message {
border: 2px solid;
margin-bottom: 1em;
padding: 1em;
text-align: center;
}
.message-alert {
@extend %message;
@include message-colors($yellow);
}
.message-error {
@extend %message;
@include message-colors($red);
}
.message-notice {
@extend %message;
@include message-colors($green);
}
.message-alert,
.message-error,
.message-notice, {
border: 2px solid;
margin-bottom: 1em;
padding: 1em;
text-align: center;
}
.message-alert {
border-color: goldenrod;
color: goldenrod;
}
.message-error {
border-color: darkred;
color: darkred;
}
.message-notice {
border-color: green;
color: green;
}
The best advice would be: if you need variables, use a mixin. Otherwise, extend a placeholder.
There are two reasons for this:
@import
it in your main .scss
file.%clearfix
and @extend
it on the appropriate selectors..clearfix
class from the markup..navlist--inline
class like the one on the right.@extend
that class on elements that match the pattern.
.navlist--inline {
list-style-type: none;
li {
display: inline-block;
}
a {
display: block;
}
}
sass --watch --style expanded sass/application.scss:css/application.css
Math & Color
+
addition-
subtraction*
multiplication/
division%
modulus
$title: "Girl Develop It";
$base-font-size: 16px;
$padding-default: 2em;
.header {
padding: $padding-default / 2;
font-size: $base-font-size * 2;
&:before {
content: "Welcome to " + $title;
}
}
* Sass attempts to operate on mismatched units, but will throw an error if incompatible
Good: 20px * 2
| Bad: 20px + 4em
CSS allows /
to be used for separating values.
font: normal 1.5em/1.25 Tahoma, Arial, sans-serif;
SassScript maintains this support.
There are three alternative ways to trigger division:
$padding / 2
(350 / .25)
2 + 3 / 10
Sass also comes with a set of math functions:
abs($num)
- absolute valueceil($num)
- round up to closest whole numberfloor($num)
- round down to closest whole numberpercentage($num)
- convert to percentageround($num)
- round to closest whole number max($list)
- maximum list value min($list)
- minimum list valueWe can also use math to manipulate colors.
Math operations on colors work on pieces (r, g, b).
.addition {
color: #555555 + #112233; // => #667788
}
.subtraction {
color: #555555 - #112233; // => #443322
}
.multiplication {
color: #555555 * 2; // => #aaaaaa
}
.division {
color: (#555555 / 2); // => #2a2a2a
}
Sass comes with a set of color functions.
rgba($hex, $alpha)
lighten($color, $percent)
darken($color, $percent)
saturate($color, $percent)
desaturate($color, $percent)
mix($color1, $color2)
grayscale($color)
invert($color)
complement($color)
Here's are two awesome tools to help visualize color output.
.wrapper
and .kitten
.news
, .events
, and .location
sass --watch --style expanded sass/application.scss:css/application.css
Directive
Sass let's us define
custom functions.
@function fluidize($target, $context) {
@return ($target / $context) * 100%;
}
.sidebar {
width: fluidize(350px, 1000px);
// => width: 35%;
}
@function
directive.@return
directive.Sass let's us set
conditions.
$theme: light !default;
body {
@if $theme == dark {
background: #000;
} @else if $theme == dark-mid {
background: #4f4f4f;
} @else if $theme == light-mid {
background: #afafaf;
} @else {
background: #fff;
}
}
@if
directive, then give it a comparison to evaluate@else if
for additional comparisons@else
as a fallback if all prior comparisons evaluate to null
or false
Comparators include:
* only compare numbers
==
equal to>
greater than *>=
greater than or equal to *!=
not equal to<
less than *<=
less than or equal to *Sass let's us
loop through lists.
$themes: dark dark-mid light-mid light;
@each $theme in $themes {
.theme-#{$theme} {
@include theme-colors($theme);
}
}
// .theme-dark {
// background: #000;
// color: #fff;
// }
// .theme-dark-mid {
// background: #444;
// color: #eee;
// }
// ...
@each
directive takes the form of:
@each $var in <list>
$var
can be any variable name<list>
could be a list, or a variable storing oneSass lets us loop
through # ranges.
@for $i from 1 through 12 {
.grid-col-#{$i} {
width: $grid-col-width * $i;
}
}
// .grid-col-1 {
// width: 60px;
// }
// .grid-col-2 {
// width: 120px;
// }
// ...
@for
loops;
@for $var from <start> through <end>
@for $var from <start> to <end>
$var
can be any variable name<start>
and <end>
can be an integer, or a variable storing oneSass lets us
loop conditionally.
$i: 1;
@while $i <= 12 {
.grid-col-#{$i} {
width: $grid-col-width * $i;
}
$i: $i + 1;
}
// .grid-col-1 {
// width: 60px;
// }
// .grid-col-2 {
// width: 120px;
// }
// ...
@while
directives take the form of:
@while <comparison>
false
<comparison>
variable will often be an integer that you increment, but it could be any data type that you modify as you goA little bit of list amazesauce for you.
via Roy Tomeij & Sass Conf 2013
Enjoy some snacks!
courtesy of
Mixins (@include)
Inheritance (@extend)
Math & Color
Functions
Conditionals
Loops
Questions?
Media Queries
@media screen and (max-width: 500px) {
.sidebar {
float: none;
width: 100%;
}
}
Media queries allow you to conditionally load styles based on things like page width, orientation, and even environmental lighting.
Responsive Web Design!
You nest declarations within media queries.
.sidebar {
float: left;
width: 35%;
@media screen and (max-width: 800px) {
width: 25%;
}
@media screen and (max-width: 500px) {
float: none;
width: 100%;
a { display: block; }
}
}
In Sass, you can nest your media queries under any declaration.
OMG THIS IS SO AWESOME.
It becomes easy to see exactly how the module is morphing through breakpoints.
Media Queries "bubble up" beyond their containing declarations.
.sidebar {
float: left;
width: 35%;
@media screen and (max-width: 800px) {
width: 25%;
}
@media screen and (max-width: 500px) {
float: none;
width: 100%;
a { display: block; }
}
}
.sidebar {
float: left;
width: 35%;
}
@media screen and (max-width: 800px) {
.sidebar {
width: 25%;
}
}
@media screen and (max-width: 500px) {
.sidebar {
float: none;
width: 100%;
}
.sidebar a { display: block; }
}
$break-large: 800px;
$break-small: 500px;
.sidebar {
float: left;
width: 35%;
@media screen and (max-width: $break-large) {
width: 25%;
}
@media screen and (max-width: $break-small) {
float: none;
width: 100%;
a { display: block; }
}
}
Breakpoints are often obscure numbers, and we repeat them all over the place.
Of course, we should make them variables!
$mobile-med: "screen and (min-width: 480px)";
$tablet-port: "screen and (min-width: 768px)";
.sidebar {
float: left;
width: 35%;
@media #{$tablet-port} {
width: 25%;
}
@media #{$mobile-med} {
float: none;
width: 100%;
a { display: block; }
}
}
Why not make the entire media query a variable?
We have to interpolate#{}
our variables so that Sass compiles them.
@mixin respond-to($breakpoint) {
@if $breakpoint == tablet-large {
@media only screen and (max-width: $width-large) {
@content;
}
} @else if $breakpoint == mobile-large {
@media only screen and (max-width: $width-small) {
@content;
}
}
}
.sidebar {
width: 35%;
@include respond-to(tablet-large) {
width: 25%;
}
@include respond-to(mobile-large) {
width: 100%;
a { display: block; }
}
}
The @content
directive allows us to pass entire blocks of styles to a mixin, which then outputs that content back into the declaration that called the mixin.
WAT?
Declarations created outside of a media
query cannot be @extend
ed within it :(
Output can get rather lengthy :(
.sidebar {
float: left;
width: 35%;
}
@media only screen and (max-width: 760px) {
.sidebar {
width: 25%;
}
}
@media only screen and (max-width: 400px) {
.sidebar {
float: none;
width: 100%;
}
.sidebar a {
display: block;
}
}
.footer {
height: 100px;
}
@media only screen and (max-width: 760px) {
.footer {
height: 75px;
}
}
@media only screen and (max-width: 400px) {
.footer {
height: 50px;
}
}
Sass doesn't group like media queries.
Sometimes it's best not to nest your media queries so granularly.
Keep an eye on your output!
Turn this CSS Sass via the example methods below.
You get to decide the best way to organize and nest things.
@media screen and (max-width: 480px) {
body { text-align: center; }
.wrapper { width: 100%; }
.search { margin-top: 1em; }
.header nav, .footer nav,
.footer .copy, .search {
float: none;
}
.kittens li,
.news, .events, .location {
float: none;
padding: 1em 0;
width: 100%;
}
}
@media screen and (max-width: $mobile-large) {
// ...
}
@media #{$tablet-small} {
// ...
}
@mixin respond-to($breakpoint) {
@if $breakpoint == desktop-large {
@media only screen and (max-width: $width-large) {
@content;
}
} @else if $breakpoint == mobile-large {
// ...
}
}
Modular Architecture
Modular architecture is the abstraction of repetition into "objects".
.header
, .footer
, .comment
, .button
, etc.
.media
, .media__img
, .media__body
, etc.
.btn--primary
, .btn--small
, .btn.is-selected
, etc.
ALL THE CATEGORIZATION!!
.btn
, .btn--primary
, .btn--disabled
.content
, .news__title
, .callNow
.grid__col--9
, .btn--small
, .btn--primary
.btn--large
. BOOM.
My Hybrid Sass System
OOCSS / SMACSS / Atomic / BEM
Libraries & Tools
"Compass is an open-source CSS Authoring Framework." ~ Compass
"A simple and lightweight mixin library for Sass." ~ Bourbon
"Responsive grids for Compass." ~ Susy
Media Queries
Modular Architecture
Libraries
Tools
Questions?
#SassShop
map-get($map, $key)
selector-append(&, '--mod')
@at-root
Directive@each
loops@each $animal, $color in $animals {}
@warn
, @debug
, @error
/* */
and //
@mixin
s and @media
queries.@extend
too much.#SassShop