Fasten RWD Development
with Sass
Sven Wolfermann | maddesigns
Sven Wolfermann | maddesigns
old
new
In order to install and run Sass, you need to have Ruby installed on your system.
Easy! Ruby is built in :)
if not installed, use the package manager
$ sudo apt-get install ruby
use http://rubyinstaller.org/ to install ruby
$ sudo gem install sass
install beta version:
$ sudo gem install sass --pre
already installed Sass?
check with
$ sass --version
Sass has two syntaxes.
The new main syntax is known as “SCSS” (for “Sassy CSS”).
SCSS files use the extension .scss
.
The second, older syntax is known as the indented syntax (or just “Sass”).
Instead of brackets and semicolons,
it uses the indentation of lines to specify blocks.
Files in the indented syntax use the extension .sass
.
section { margin: 1em 0; header { background-color: lightpink; } }
section margin: 1em 0 header background-color: lightpink
open terminal
$ sass input.scss output.css
watch folder
$ sass --watch sass:css
watch file
$ sass --watch sass/style.scss:css/style.css
many GUIs for compiling
and build in in many text editors or IDEs
h1 { border-bottom: 2px solid #000000; // black color: #FF8700; // orange margin: 0 0 0.5em; }
change to global variables
$brand-color1: #000000; $brand-color2: #FF8700; h1 { border-bottom: 2px solid $brand-color1; color: $brand-color2; margin: 0 0 0.5em; }
variables can be colors, sizes, percentage, ...
$page_max_width: 1200px; $padding: 20px;
.container { min-height: 100%; max-width: $page_max_width; width: auto; margin: 0 auto; padding: 0 $padding; }
SCSS
.container { max-width: $page_max_width - $padding * 2; padding: 0 $padding; ... }
CSS
.container { max-width: 1160px; /* 1200px - 20px * 2 */ padding: 0 20px; ... }
Old box model calculation
writing long selectors is time consuming
short selectors are better in general
CSS
nav {float: right;} nav li {float: left;} nav li a {color: #666;} nav li a:hover {color: #333;} nav li.current {font-weight: bold;}
SCSS
nav { float: right; li { float: left; a { color: #666; &:hover { color: #333; } } &.current { font-weight: bold; } } }
Sass nesting != HTML nesting
be careful with nesting!
you can run into performance issues with long selectors
div { color: black .foo { color: black } // descendant + .foo { color: black } // adjacent // sibling > .foo { color: black } // child ~ .foo { color: black } // general // sibling & .foo { color: black } // Sass' parent // selector &.bar { color: black } &:hover { color: black } }
div { color: black; } div .foo { color: black; } div + .foo { color: black; } div > .foo { color: black; } div ~ .foo { color: black; } div .foo { color: black; } div.bar { color: black; } div:hover { color: black; }
the & (ampersand) has a placeholder function for the parental selector
a { &:hover, &:focus { color: black } }
a:hover, a:focus { color: black; }
Usecase for Modernizr classes
div { box-shadow: 0 0 5px rgba(#000, 0.8); // Sass feature for Hex to RGB colors .no-boxshadow & { border: 1px solid #555; } }
div { box-shadow: 0 0 5px rgba(0, 0, 0, 0.8); } .no-boxshadow div { border: 1px solid #555; }
div { .parent & .child { color: black } }
.parent div .child { color: black; }
the way in CSS
/* style.css */ @import "base.css"; @import url("styles.css"); @import url("druck.css") print;
Importing CSS files into one file can cause performance issues
Limit your external references in your HTML
split your stylesheet in many chunks and use the import function of Sass
@import "modules/base"; @import "partials/header", "partials/footer";
create subfolders and devide into partials
use underscore in your filenames to concatinate the partials within the compiling process
Imagine this structure
/style.sass /modules/ ┗ _normalize.sass ┗ _base.sass ┗ _mixins.sass /partials/ ┗ _footer.sass ┗ _header.sass /ie.sass /print.sass
none underscore files will be compiled into seperate CSS files
# style.sass @import modules/normalize @import modules/base @import modules/mixins @import partials/header @import partials/footer @import ie @import print
this compiles to 3 files:
/css ┗ style.css ┗ ie.css ┗ print.css
@extend clones the attributes from rules and adds them to another rule.
.button { background-color: $color-main; font-weight: bold; color: white; padding: 5px; }
Then we can @extend the class to another
.button-checkout { @extend .button; background-color: darken($color-main, 20%); }
.button-checkout { @extend .button; background-color: darken($color-main, 20%); .msg & { @extend .button; background-color: darken($color-main, 30%); } }
.button, .button-checkout, .msg .button-checkout { background-color: blue; font-weight: bold; color: white; padding: 5px; } .button-checkout { background-color: #000099; } .msg .button-checkout { background-color: #000066; }
// This ruleset won't be rendered on its own. %button { color: blue; font-weight: bold; font-size: 2em; }
placeholder selectors can be extended, just like classes and IDs. The extended selectors will be generated, but the base placeholder selector will not
.btn-notice { @extend %button; }
.btn-notice { color: blue; font-weight: bold; font-size: 2em; }
placeholder selectors will not be rendered to CSS.
%button { background-color: $color-main; font-weight: bold; color: white; padding: 5px; } .button-checkout { @extend %button; background-color: darken($color-main, 20%); }
.button-checkout { background-color: #000099; font-weight: bold; color: white; padding: 5px; }
Man, tell me the cool things!
Are code snippets (reusable elements)
Parameterizable (use reasonable defaults)
@mixin border-radius($value) { -webkit-border-radius: $value; -moz-border-radius: $value; border-radius: $value; } .box { color: $color-main; font-family: $helvetica-font-stack; @include border-radius(5px); }
compiled to
.box { color: blue; font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; }
but thats a bad example – no need for the vendor prefixes of border-radius anymore
only use border-radius: 5px;
in your stylesheets
Relational operators (<, >, <=, >=) evaluate numbers
1 < 20 // true 10 <= 20 // true 4 > 1 // true 4 >= 1 // true
Comparison operators (==, !=) evaluate all data types
1 + 1 == 2 // true small != big // true #000 == black // true
@if
@for
@each
@while
$theme: ocean; div { @if $theme == dusty { background: #c6bba9; color: $color; } @else if $theme == ocean { background: blue; color: white; } }
@media queries in place
aside { width: 100%; @media screen and (min-width: 680px) { width: 25%; } }
aside { width: 100%; } @media screen and (min-width: 680px) { aside { width: 25%; } }
use main breakpoints as variables
$break-small: 320px; $break-large: 1200px; .profile-pic { float: left; width: 100px; @media screen and (min-width: $break-small) { width: 250px; float: none; } @media screen and (min-width: $break-large) { float: right; } }
$break-small: 320px; $break-large: 1200px; @mixin respond-to($media) { @if $media == smartpones { @media only screen and (max-width: $break-small) { @content; } } @else if $media == tablet { @media only screen and (min-width: $break-small + 1) and (max-width: $break-large - 1) { @content; } } @else if $media == desktop { @media only screen and (min-width: $break-large) { @content; } } }
Usage
// profile picture module .profile-pic { display: block; @include respond-to(smartpones) { float: left; width: 125px; } @include respond-to(tablet) { width: 125px; } @include respond-to(desktop) { width: 33%; } }
CSS output sample
.profile-pic { display: block; } @media only screen and (max-width: 320px) { .profile-pic { float: left; width: 125px; } } @media only screen and (min-width: 321px) and (max-width: 1199px) { .profile-pic { width: 125px; } } @media only screen and (min-width: 1200px) { .profile-pic { width: 33%; } }
Writing mobile-first styles without leaving IE < 9 behind
Media Query Mixin:
// all.scss $fix-mqs: false !default; @mixin respond-min($width) { // If we're outputting for a fixed media query set... @if $fix-mqs { // ...and if we should apply these rules... @if $fix-mqs >= $width { // ...output the content the user gave us. @content; } } @else { // Otherwise, output it using a regular media query @media screen and (min-width: $width) { @content; } } } // and a respond-max mixin, that does what you might expect
OldIE Mixin:
// all.scss $old-ie: false !default; @mixin old-ie { // Only use this content if we're dealing with old IE @if $old-ie { @content; } }
// all-old-oldie.scss $old-ie: true; $fix-mqs: 65em; @import 'all';
To give the CSS to the browsers, use good old conditional comments:
<!--[if lte IE 8]> <link rel="stylesheet" href="css/all-old-ie.css"> <![endif]--> <!--[if gt IE 8]><!--> <link rel="stylesheet" href="css/all.css"> <!--<![endif]-->
Sourcemaps to work with original files in developer tools
scss --sourcemap sass/styles.scss public/styles.css
Include in Sass:
/*# sourceMappingURL=styles.css.map */
Sassmaps output
{ "version": "3", "mappings": "4DAUA,qFAWQ,CACJ,OAAO,CAAE,KAAK,CAOlB,…", "sources": ["../sass/_vars.css”,”../sass/styles.scss"], "file": "styles.css" }
Compass is to Sass like jQuery to Javascript
Compass is a Framework, that extends Sass
It brings a lot of CSS3 mixins and useful CSS stuff
current version is a bit outdated, wait for 1.0
Github List of Compass Plugins
add to the config.rb
# config.rb ... require 'compassplugin'
@import 'compassplugin';
$ sudo gem install singularitygs $ compass create myproject -r singularitygs --using singularitygs
Add Singularity plugin to existing projects
# config.rb require "singularitygs"
@import 'singularitygs';
$grids: 12; $gutters: 1/3;
$grids: 12; $gutters: 0.9375em; $gutter-style: split;
$grids: 1 4 1; $gutters: 1/6; $gutter-style: split;
Isolation grids as default
@include grid-span($span, $location);
// Span 1 column, starting at the 2nd column .span1-pos2 { @include grid-span(1, 2); } // Span 3 columns, starting at the 3rd column .span3-pos3 { @include grid-span(3, 3); } // Span 5 columns, starting at the 7th column .span5-pos7 { @include grid-span(5, 7); }
proportional Grids (Golden Grid)
$ gem install breakpoint
Add plugin to projects
# config.rb require "breakpoint"
// main.scss @import "breakpoint";
// basic media queries, min-width and min/max width $basic: 543px; $pair: 456px 794px; .foo { content: 'No Media Queries'; @include breakpoint($basic) { content: 'Basic Media Query'; } @include breakpoint($pair) { content: 'Paired Media Query'; } }
/* Nested Breakpoint calls become separate media queries */ .foo { content: 'No Media Queries'; } @media (min-width: 543px) { .foo { content: 'Basic Media Query'; } } @media (min-width: 456px) and (max-width: 794px) { .foo { content: 'Paired Media Query'; } }
$breakpoint-to-ems: true; $media: 'screen' 700px; #foo { @include breakpoint($media) { content: 'Media'; } }
@media screen and (min-width: 43.75em) { #foo { content: 'Media'; } }
@import "breakpoint"; .sample { color: red; @include breakpoint(min-resolution 2dppx) { color: green; } }
.sample { color: red; } @media (min-resolution: 2dppx), (-webkit-min-device-pixel-ratio: 2), (min--moz-device-pixel-ratio: 2), (min-resolution: 192dpi) { .sample { color: green; } }
// _layout.scss // global breakpoints $small: 24em; $middle: 34em; $large: 65em, 'no-query' true; .component { color: red; @include breakpoint($middle) {color: blue} @include breakpoint($large) {color: green;} }
// styles.scss $breakpoint-no-queries: false; $breakpoint-no-query-fallbacks: false; @import 'layout';
// fallback-ie8.scss $breakpoint-no-queries: true; @import 'layout';
styles.css
.component { color: red; } @media (min-width: 34em) { .component { color: blue; } } @media (min-width: 65em) { .component { color: green; } }
fallback-ie8.css
.component { color: red; color: green; }
easy embedding images with fixed ratios or iframes
.some-class { @include fixed-ratiobox(16/9, 100%, 'img'); }
.some-class { position: relative; height: 0; } .some-class img { display: block; position: absolute; width: 100% !important; height: 100% !important; top: 0; margin: 0; padding: 0; } .some-class { padding-top: 56.25%; width: 100%; }
lot of Sass goodies
Sven Wolfermann | maddesigns