Wifi Network:
Password:
o
key to see/navigate an overview of slides.JulieCameron.com | @JewlOfTheLotus | jewlofthelotus@gmail.com
Client: Kittens "R" Us
Description: A local adoption listing website for kitties in need of homes.
Framework: HTML5 & CSS3
Exercise Files: github.com/jewlofthelotus/SassShop-exercises
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.Why Sass?
.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
More info at: sass-lang.com/install
(aka Compiling Sass)
Sass CSS
$ sass source.scss output.css
$ sass source_dir output_dir
$ sass sass/application.scss css/application.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.
css/application.css
to sass/application.scss
mkdir sass && mv css/application.css sass/application.scss
sass --watch sass/application.scss:css/application.css
index.html
in the browser and verify the new Sass-based styles are loading properly.
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.
General Recommendation: 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..header
, .footer
, and .kittens
are good places to start!
P.S. Don't Forget
The parent &
selector.
Oh! Totoally feel free to refactor!.news
, .events
, & .location
are ripe for pattern abstraction!
// good
.navigation {
li {//...}
a {
//...
&:hover { //... }
}
}
// bad
body {
.header {
.navigation {
ul {
li {
a {
// ...
}
}
}
}
}
}
sass --watch 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/application.scss css/application.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 and settings_all.scss
is often used to import collections of similar imports
// 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";
You can DRY up @import
s by grouping them together with comma separation.
// Variables
@import "vars";
// Libraries
@import "lib/compass",
"lib/fontawesome";
// Application
@import "base/all",
"modules/all",
"pages/all";
application.scss
to more modular / organized locations.application.scss
to @import
your new partials.sass --watch 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;
}
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"
}
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;
}
Create a vars
partial in your base directory and @import
it in your main .scss
file.
DRY Up Your Styles
Add variables for things like:
Repetitious values.
Arbitrary values.
(Code smells.)
sass --watch sass/application.scss:css/application.css
Math & Color
+
addition-
subtraction*
multiplication/
division%
modulus
$title: "SassShop";
$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 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 sass/application.scss:css/application.css
Enjoy some snacks!
courtesy of
About Sass
Workflow
Nesting
Imports & Organization
Variables
Math & Color Utilities
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 sass/application.scss:css/application.css
exercises/kittens/index.html
in your browser.exercises
directory in your file browser.kittens
, create a new folder called "day1".kittens
into the new day1
folder.index.html
, sass/
, and css/
.kittens-4-math
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;
}
.footer {
border-radius: 5px;
padding: 5px 20px;
}
.feature a {
border-radius: 5px;
padding: 5px 20px;
}
.content {
border-radius: 5px;
padding: 5px 20px;
}
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;
}
@mixin margin--horz($left, $right: $left) {
margin-left: $left;
margin-right: $right;
}
@mixin margin--vert($top, $bottom: $top) {
margin-bottom: $bottom;
margin-top: $top;
}
.module { @include margin--vert(1em, 3em); }
$icon-plus: "\e600";
$icon-minus: "\e601";
// ...
@mixin icon($name) {
@extend %font--icon;
content: $name;
}
.module:before {
@include icon($icon-plus);
}
@mixin triangle($dir, $border-color, $width: $triangle--width, $height: $triangle--width/2) {
$vert: 0;
$horz: 0;
$edge: top;
$oedge: bottom;
@if $dir == "up" {
$edge: bottom;
$oedge: top;
$s1: $width;
$s2: $height;
$vert: $s2;
$horz: $s1 / 2;
} @else if $dir == "down" {
$edge: top;
$oedge: bottom;
$s1: $width;
$s2: $height;
$vert: $s2;
$horz: $s1 / 2;
} @else if $dir == "right" {
$edge: left;
$oedge: right;
$s1: $height;
$s2: $width;
$vert: $s2 / 2;
$horz: $s1;
} @else {
$edge: right;
$oedge: left;
$s1: $height;
$s2: $width;
$vert: $s2 / 2;
$horz: $s1;
}
@include transform(scale(.9999)); // Firefox jagged lines fix
display: block;
width: 0;
height: 0;
border-style: solid;
border-width: $vert $horz;
border-color: transparent; // IE8 Fallback
border-color: rgba(0, 0, 0, 0); // actually transparent (for Firefox)
border-#{$edge}-color: $border-color;
border-#{$oedge}-width: 0;
}
.thing:before { @include triangle("up", #eeeeee, 25px); }
mixins
partial in your base directory and @import
it in your main .scss
file.@mixin
for scaled transformations with a required scale amount argument. Include common vendor prefixes.transform: scale(1.1);
sass --watch 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;
}
%hoverTransition {
transition-property: background, color, font-size, line-height, opacity, border, padding, margin;
transition-duration: .1s;
transition-timing-function: linear;
}
%disabled {
@include opacity(.5);
cursor: not-allowed;
}
%muted {
@include opacity(.3);
}
%font--main--family { font-family: $focus-font-family; letter-spacing: .015em; }
%font--main { @extend %font--focus--family; font-weight: 500; }
%font--main--medium { @extend %font--focus--family; font-weight: 600; }
%font--main--bold { @extend %font--focus--family; font-weight: 700; }
%font--main--black { @extend %font--focus--family; font-weight: 800; }
%copy--small {
font-size: $font-size-small;
line-height: $line-height-small;
}
%copy--medium {
font-size: $font-size-medium;
line-height: $line-height-medium;
}
%copy--large {
font-size: $font-size-large;
line-height: $line-height-large;
}
%btn--primary {
background: $btn--primary--bg;
border-color: $btn--primary--border;
color: $btn--primary--color;
}
// ...
@mixin button-setup($type) {
@extend %btn;
@extend %btn--#{$type};
&:focus { @extend %btn--#{$type}; }
&:hover { @extend %btn--#{type}--hover; }
&.disabled {
@extend %disabled;
&:hover { @extend %btn--#{$type}; }
}
}
.btn--primary { @include button-setup("primary"); }
.btn--secondary { @include button-setup("secondary"); }
.btn--tertiary { @include button-setup("tertiary"); }
// background: $btn--#{$type}--bg; => Undefined variable: "$btn--"
sooooo when do I use these...?
Are mixins without arguments bad?
Since they duplicate code, on both sides?
@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 {
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;
}
Is this better?
%rounded-box {
border-radius: 5px;
padding: 5px 20px;
}
.header {
@extend %rounded-box;
}
.footer {
@extend %rounded-box;
}
.feature a {
@extend %rounded-box;
}
.content {
@extend %rounded-box;
}
.header,
.footer,
.feature a,
.content {
border-radius: 5px;
padding: 5px 20px;
}
Is this better?
.header, .footer, .feature header a, .content, #taco ul a, .hipster dl,
[class="btn--"], .comment .comment__body .comment__quote, .card__header,
.card--standard, .card--transFooter, .card--featured, .card--scroller,
.card--static, .card--ebook, .card--transFooter .card__content
.container .wrapper, .link--fancy, #badthing, .nope.probably a.not span.good,
article header, .article .header, [class*='rabbit'], .party .at .codemash,
#hashtag, .dot, [class*="hooligans"], [class*='making'].up.classes.is-hard,
.julie + .rocks, #made.up, .bourbon, a div i span, [id="where-the-party"],
.this .could [class*="actually-happen"], #actually.it .happens.alot,
#seriously [class*="this"].is a.problem, .it .can.get #really .out-of-hand {
border-radius: 5px;
padding: 5px 20px;
}
Is this actually bad?? ....what if it's gzipped?
.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;
}
It's complicated.
placeholders
partial in your base directory and @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 sass/application.scss:css/application.css
Directives
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;
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 goMaps
Maps give us key-value pairings.
$mymap: (
key1: value1,
key2: value2,
key3: value3
);
map
functions.
$eye_color: (
jeremy: #0989cb,
beth: #8666ae,
sarah: #02bba7
);
$font: (
jeremy: Helvetica,
beth: Verdana,
sarah: Times
);
@each $key, $value in $eye_color {
.#{$key} {
background: $value;
font-family: map-get($font, $key);
}
}
.jeremy {
background: #0989cb;
font-family: Helvetica;
}
.beth {
background: #8666ae;
font-family: Verdana;
}
.sarah {
background: #02bba7;
font-family: Times;
}
map-get($map, $key)
map-merge($map1, $map2)
map-remove($map, $keys...)
map-keys($map)
map-values($map)
map-has-key($map, $key)
Putting it all together.
$icons: (
checkmark: "\e600",
plus: "\e601",
minus: "\e602"
);
@each $name, $value in $icons {
.icon--#{$name} {
&:before { content: $value; }
}
}
$messages: (
error: (#d82d2d, #666),
success: (#52bf4a, #fff),
warning: (#c23435, #fff)
);
@each $name, $colors in $messages {
$bg: nth($colors, 1);
$color: nth($colors, 2);
.message--#{$name} {
background-color: $bg;
color: $color;
}
}
$layers: (
bottom: 1,
middle: 5,
top: 10
);
@mixin layer($name) {
z-index: map-get($layers, $name);
};
.overlay {
@include layer(middle);
}
.lightbox {
@include layer(top);
}
$font--sizes: (
( base, $base-font-size, $base-line-height ),
( small, $base-font-size*.75, $base-line-height*.75 ),
( large, $base-font-size*1.5, $base-line-height*1.5 )
);
@mixin typeset($level) {
@each $item in $font--sizes {
@if $level == nth($item, 1) {
font-size: nth($item, 2);
line-height: nth($item, 3);
}
}
}
@each $set in $font--sizes {
$size: nth($set, 1);
#{"%size--" + $size} {
@include typeset($size);
}
}
$themes: (
gray: (
button-bg: #ccc,
button-color: #f2f2f2
),
brown: (
button-bg: #ab906b,
button-color: #ecdac3
)
);
@function setStyle($theme, $property) {
@return map-get(map-get($themes, $theme), $property);
}
.btn {
@each $key, $value in $themes {
.theme--#{$key} & {
background: setStyle($key, button-bg);
color: setStyle($key, button-color);
}
}
}
A little bit of list amazesauce for you.
via Roy Tomeij & Sass Conf 2013
I failed to come up with an exercise for the kittens site.
Look for things like this in your code:
Play around with this cool stuff:
sass --watch sass/application.scss:css/application.css
Enjoy some snacks!
courtesy of
Mixins (@include)
Inheritance (@extend)
Functions
Conditionals
Loops
Maps
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?
Class & placeholder 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 one of 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%;
}
}
// Option 1 - dimension variable
@media screen and (max-width: $mobile-large) {
// ...
}
// Option 2 - media query variable
@media #{$tablet-small} {
// ...
}
// Option 3 - mixin with @content
@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.
.btn
, .btn--primary
, .btn--disabled
.content
, .news__title
, .callNow
.grid__col--9
, .btn--small
, .color--primary
.btn--large
. BOOM.
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
About Sass
Workflow
Nesting
Imports & Organization
Variables
Math & Color Utilities
Mixins (@include)
Inheritance (@extend)
Functions
Conditionals
Loops
Media Queries
Modular Architecture
Libraries
Tools
Questions?
#SassShop
selector-append(&, '--mod')
@at-root
Directive@warn
, @debug
, @error
/* */
and //
@mixin
s and @media
queries.@extend
too much.