On Github jewlofthelotus / SassShop
Wifi Network: atomic-detroit-public
Password: atomicpublic
Girl Develop It is here to provide affordable and accessible programs to learn software through mentorship and hands-on instruction.
JulieCameron.com | @JewlOfTheLotus | jewlofthelotus@gmail.com
What is your name?
What type of development experience do you have?
What is your favorite kind of cookie?
Well you all rule because I love all cookies, my nickname as a child was "the cookie monster."
#SassShopDetroit
First sass class in Detroit
You're all very cutting edge right now
Client: Kittens "R" Us
Description: An adoption listing website for local kitties in need of homes.
Documentation: Setup Instructions
Slides: gdidetroit.com/sass
Nothing too special, just something to play around with - but feel free to go all out!
I HIGHLY recommend that you DL these slides and refer back to them as we're going through exercises
You're welcome to follow the examples exactly, or customize things as we go.
And Feel free to try things out as I'm presenting, I don't want you to feel like you need to wait for the exercise to experiment.
What is Sass?
Syntactically Awesome StyleSheets
Like CSS but BETTERIt addresses the previously mentioned shortcomings
PRO TIP It's Sass, not SASS
Sass Trifecta of Awesome
Tools like Sass, Less and Stylus have live-tested features and impelemnations that will likely inspire features in future CSS specifications.
So let's get a taste of what Sass can do for us by digging into one of CSSs pain-points: repetition.
.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
development principle aimed at reducing repetition
HINT: the syntax highlighting might give you a few ideas
Colors >> Padding >> Selectors
So let's see what Sass can do for us in these scenarios, starting with colors...
.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; }
No one wants to remember #531946
Wouldn't it be nice if we had a way to store and reference our colors?
.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; // ... }
It looks like we've got a default box style
What if we could set those defaults in one place and reference it elsewhere?
.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 { // ... }
What if I want to change .header to .banner? I have to edit it everywhere
What if we had a way of only referencing these repeated selectors once?
What if we could nest our selectors just like we can in our HTML?
.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; }
Colors (vars), Border Radius + Padding (mixin), Selectors (nesting)
Alright, so now that we've gotten a taste of it in action, let's get an official definition
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 DocumentationAny valid CSS file is a valid Sass file
CSS Extension
SassScript Language
CSS Preprocessor
SassScript Interpreter
Sass CSS
Sass is an extension of CSS, but we can't load Sass in the browser like we can CSS
May seem like 2 separate tools, but they're really just one, and more often then not, you'll hear them referred to as a singluar tool.
.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
Because valid CSS is also valid SCSS, getting started with Sass is as easy as changing the file extension.
Start slow, add in features as you learn / get more comfortable.
The Sass Workflow
$ gem install sass
Sass CSS
$ sass source.scss output.css
$ sass source_dir output_dir
So let's get a more visual representation...
$ sass sass/screen.scss css/screen.css
At this point you might be thinking
Well gosh, do I need to run this command EVERY single time I make a change?
And the answer is of course not! That's what watching is for!
I'm going to get into some code to explain the style option more...
Exercise - Section 2
Sass Basics
Alright, so now that we know generally what Sass is and how it works
let's get into the basics
Imports are critical for a modular and organized architecture
<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; } }
THIS IS MY FAVORITE THING.
It's the one thing that I instantly miss when writing vanila CSS.
I do it by habit and intuition now.
Let's look at the output of that...
.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; }
So, there's kid of a gotchya with nesting though...
body { .header { .navigation { ul { li { a { // ... } } } } } }
body .header .navigation ul li a { // ... }
Work WITH the cascade, not against it
Nesting too deeply results in specificity nightmares and fragile, unmaintainable code
<header class="header"> <h1>My Site Title</h1> <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> </header>
RememberDon't nest too deeply!
PRO Add MOAR stylez
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 ampersand will reference the entire chain of parent selectors
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; }
So this can be really really helpful, especially if you're familiar with the concept of modifiers as they relate to the concepts of OOCSS and BEM.
What you're essentially doing is modifying the parent selector
We'll jump quickly into OOCSS and BEM later, but you'll see hints of it throughout the shop
<footer class="footer"> <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> </footer>
** You may need to readjust your nesting
Alright, so before we move on from nesting there is one more thing I'd like to cover
You can also nest namespaced properties
a { border: { color: #deedee; style: solid; width: 2px; } }
a { border-color: #deedee; border-style: solid; border-width: 2px; }
Not only can you nest selectors
ALRIGHT, so that was nesting, let's move on to the next basic...
Variables
We already saw a sampling of variables when we talked about repetition, so we know that they help to
So let's see what ELSE we can use variables for
you can put just about anything in a variable and so for that reason, it's a very very valuable tool for dealing with the repetition and tediousness of CSS
so how do we use em...Variables are defined like normal CSS rules(property: value;), only prefixed with a $
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; }
as you can see, we've set a few different types of data - there a total of 6 data types that Sass accepts
$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;
NUMBERS can be set with or without units
STRINGS can be set with or without quotes
we'll get into things like lists, booleans, and null later, but they can be super helpful
now, there are two ways to define variables, the first is Simple Assignment
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; }
simple assignment: create a variable, assign it a value
you can also override these values
This is especially helpful when theming or working with third party tools
The !default flagMeans: 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; }
a lot of 3rd party libraries use guarded assignment so that you can override their values before including the code
so this leads us nicely into scope
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 green; } p { border: $border-width solid red; color: $color; // ERROR!! // Undefined variable "$color" }
This is sort of a global / local setup
Setting new values to "global" variables within a selector changes future instances of that variable
$border-width: 4px; a { $border-width: 2px; border: $border-width solid green; } p { border: $border-width solid red; //=> 2px }
We can change global variables anywhere
We can only create or change local variables within that nesting level
So this brings us to the last aspect of variables - interpolation
Variables can be injected into selectors, property names, and strings with #{$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"; }
We'll see how this can become pretty handy later on
$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; }
You want to be crafty with naming, but also don't spend too much time worrying about them, they're easy enough to update later once patterns become more apparent
* Hint: Set footer background on html so it fills the entire bottom of the page
** Try a fancy font or two from google!
PRO Add MOAR stylez
So we're not going to focus too much on globals or overrides or interpolation at this stage, let's just get comfortable with setting and using variables
Imports & Organization
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 @imports 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";
Now when I say MAIN SASS FILE we're starting to get into file organization here...
@import "vars"; @import "lib/compass"; @import "lib/fontawesome"; @import "utilities"; @import "grid"; @import "base";
$ sass sass/screen.scss css/screen.css
$ sass sass/ css/
if we compile the whole directory, we get output for every file
running sass on directories will compile all Sass files regardless of whether or not they've been included elsewhere
how do we fix that? what if we wanted to compile a screen and a print stylesheet and didn't want to run multiple sass commands...
$ sass sass/ css/
partial help us break up our code in the
// 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";
Ultimately, how you organize your code is up to you
PRO Add MOAR stylez
Enjoy some snacks courtesy of Atomic Object!
Sass Intro
Workflow
Nesting
Variables
Imports & Organization
Questions?
Advanced Sass
Variables let you reuse single values.
Mixins let you reuse blocks of styles.
So let's look at some code...
.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; // ... }
Earlier we saw this CSS example where we're repeating the same border-radius and padding over and over
They're sort of default settings, if you will
We can make this more maintainable, by pulling these out into one location
let's dig into this code a bit more...
@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; // ... }
Now, let's look at what's actually happening here before we move on
defined before referenced -- partials / imports
So let's see what this actually outputs
@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; /* ... */ }
We're still just repeating properties here....
.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; }
It'd actually be more efficient just to group the classes together.
So what's a good use case for mixins then...
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.
How about anything with vendor prefixes?
Those things get repetitive, it'd be nice to have a one liner right?
But..
alright, so when the hell are mixins useful then?...
@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; // ... }
Mixins are great for repeated blocks of styleswhere the values differ from case to case.
I mentioned before that mixins can accept arguments
Output..
Mixins are great for repeated blocks of properties where the values differ from case to case...
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; // ... }
Arguments are required, unless you set a default
Now let's go a level deeper here...