- Syntactically Awesome Style Sheets
- An extension of CSS - compiled to CSS.
- Coded in Ruby 💥!!
- All CSS is valid Sass but all Sass is not valid CSS.
- Supports other frameworks (i.e. Bootstrap, Materialize)
- Use file extension
.scss (Sassy CSS. more commonly used syntax)
or.sass (older syntax)
. - Provides variables, nesting, mixins, and selector inheritance.
- Industry-wide = you will see it more often.
- Has a lot of features which help with ORGANIZATION!
- Allow for some principles of OOP.
- "Cascading" is confusing!
- Mostly, so other developers don't look at your frontend code like:
- Explain why SASS is a better option than raw CSS.
- Be able to set up SASS in a Rails project.
- Understand how to create variables in SASS.
- Understand how to nest elements in SASS.
- Understand how to nest properties in SASS.
- Break files up in a logical way.
- To target an element in CSS such as a
<p>
tag:p
- To target a class in CSS:
.class
- To target an id in CSS:
#id
- End a CSS statement:
;
- Cascading - What is it?
A stylesheet looks from rule to rule and calculates specificity. Specificity is basically a measure of how specific a selector is
(from the MDN docs).
- We are going to use this repository for this workshop.
- Clone the repository.
- Change directories into
sass_workshop
. - Bundle (to get all the gems!).
- Run
rake db:create db:migrate db:seed
from the terminal to setup the database with movies. - Start up a rails server (
rails s
) from the terminal and navigate to localhost:3000 to see the application. - We will use the root url for the workshop.
- There are a few things that we must do in a rails application to make Sass work the way that we want:
- Check in the
Gemfile
to see ifgem 'sass-rails'
is included. It is included in Rails versions > 3.1 but you may need to add it to earlier Rails versions. - Rename
application.css
toapplication.scss
because we will be writing Sass and we need to tell Rails that it should be compiled to CSS. We can also useapplication.scss
to provide the same benefits. - Create a
base.scss
file underassets/stylesheets
. This is where we will write our Sass code.
- Check in the
- Here is the color palette that we will be using with the exception of black, we replace it with hex #800000:
- Defining variables is fundamental to programming. One thing that was always frustrating about CSS, was the inability to reuse CSS code. NOT ANYMORE!
- To define a variable, we use a
$
to declare the variable and a:
to assign it. - Let's define a variable for the hex color #3D348B and define it for the font color of all
h1
elements and #E0E2DB as the background color.
/* base.scss */
$indigo-header: #3D348B;
$lighter-gray: #E0E2DB;
h1 {
color: $indigo-header;
background-color: $lighter-gray;
}
- Our app is pretty small right now so keeping track of colors is simple but imagine as our app grows and we have to continually look back for our hex codes. By creating variables, we can easily access these colors at any point.
- On your own:
- Define the rest of the color palette with variables.
- Assign the text color of all
h2
elements to the variable you set the hex #800000. - Create a variable called
custom-border
and assign border rules so that a 3 pixel dotted border in the color #E6AF2E appers around allp
elements.
The results should be something like:
/* base.scss */
$indigo-header: #3D348B;
$mustard: #E6AF2E;
$maroon: #800000;
$lighter-gray: #E0E2DB;
$darker-gray: #BEB7A4;
$custom-border: 3px dotted $mustard;
h1 {
color: $indigo-header;
background-color: $lighter-gray;
}
h2 {
color: $maroon;
}
p {
border: $custom-border;
}
What if we just want the <h1>
on the movies#index
to be $darker-gray
but other h1
's to be $indigo-header
?
We can NEST our html elements in the same structure the our page has. This provides more readable code and an actual STRUCTURE for our CSS. FINALLY!!!
h1 {
color: $indigo-header;
background-color: $lighter-gray;
}
h2 {
color: $maroon;
}
p {
border: $custom-border;
}
.movie-index {
h1 {
color: $darker-gray;
}
}
Run rails s
and visit the route http://localhost:3000
to see the CSS. Inspect the CSS and see that it compiles as we expect to see CSS code! COOL!
Benefits:
- Readibility
- Maintainability
- Namespaces: Pretty and concise class names
As a general rule: Do not nest more than 3 levels deep. Anything more affects the readability of the code
Another great benefit of sass is that we can nest our properties for further clarification and organization.
Let's give use the class that already exists and center our movie-show-link
in our movie-index
and strikethrough the text.
h1 {
color: $indigo-header;
background-color: $lighter-gray;
}
h2 {
color: $maroon;
}
p {
border: $custom-border;
}
.movie-index {
h1 {
color: $darker-gray;
}
.movie-show-link {
text: {
align: center;
decoration: line-through;
}
}
}
Let's navigate to our browser and inspect the movie-show-link
element on our movie-index
. When this sass code gets compiled, we see that it is automatically namespaced and if it is an actual property in CSS, it gets applied. It will try to compile, no matter what. For example, let's look at this in the browser:
.movie-index {
.movie-show-link {
text: {
align: center;
decoration: line-through;
cool: papyrus;
}
}
}
On your own, add a font family of papyrus
and a size of 30px to the <p>
tag under .movie-index
HTML and CSS deserve to be as DRY as your Ruby is. This is difficult to achieve in raw HTML and CSS.
Splitting CSS files into smaller files also requires more HTTP requests. Sass provides a benefit by compiling all files into one by using import statements to bring files into one file. While this isn't necessarily a problem in Rails because of the asset pipeline, it can potentially slow down an application.
Right now, our application.scss
file looks like this and is set up for Sprocket directives:
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
* vendor/assets/stylesheets directory can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
* files in this directory. Styles in this file should be added after the last require_* statement.
* It is generally better to create a new file per style scope.
*
*= require_tree .
*= require_self
*/
With plain css files, the above lines that begin with *=
require all stylesheets from the current directory. But with Sass, we do not want to utilize the Sprockets directives so need to modify our file. More from "Rails Guides":
If you want to use multiple Sass files, you should generally use the Sass @import rule instead of these Sprockets directives. When using Sprockets directives, Sass files exist within their own scope, making variables or mixins only available within the document they were defined in.
The directives to remove are:
*= require_tree .
*= require_self
And outside of the comment block, we import our sass file.
@import 'base';
We use quotes around the file to be imported and a ;
to finish the statement, similar to css statements.
Let's start up our browser and navigate to the root to see our BEAUTIFUL CSS!
Right off the bat, we created a base.scss
that is holding all our code. We want to make this a partial that can hold all base styles for your site. If you get more into SCSS functionalities, you can use this partial to store mixins and extensions as well.
This is a great place to store color and font variables and mixins that are used throughout the application.
Let's put font colors and font type there. We want to use a new "fun" font!
/* assets/stylesheets/base.scss */
@import url('https://fonts.googleapis.com/css?family=Joti+One');
$fun-font: 'Joti One', cursive;
$indigo-header: #3D348B;
$other-black: #191716;
$mustard: #E6AF2E;
$lighter-gray: #E0E2DB;
$darker-gray: #BEB7A4;
Let's create another file in the same directory called skeleton.scss
and place our rules in that file.
If all of our sections of the site we're building are sharing consistency among element names, consistency is assumed among styling for these elements.
We also need to import the file in our manifest file. Since the manifest file serves as the compiled css file, information will get compiled in the order it is imported and therefore, skeleton.scss
will know about base.scss
.
Our manifest file should look like this:
@import 'base';
@import 'skeleton';
And skeleton.scss
:
Let's use the fun-font
at 50px and gather all of this into an SCSS partial.
/* assets/stylesheets/skeleton.scss */
$custom-border: 3px dotted $mustard;
h1 {
font: {
family: $fun-font;
size: 50px;
}
color: $indigo-header;
background-color: $lighter-gray;
}
h2 {
color: $maroon;
}
p {
font: {
family: papyrus;
size: 30px;
}
}
.movie-index {
h1 {
color: $darker-gray;
}
.movie-show-link {
text: {
align: center;
decoration: line-through;
}
}
p {
border: $custom-border;
}
}
As stylesheets grow, mantainability becomes a concern. To alleviate this, we can use partials.
If a scss file requires another scss file, the necessary scss files will need to be imported before the scss file it is required in.
A general rule of thumb is import in order of least specific to most specific. This way, if base styles need to be overridden, they can be within their individual section.
This would allow me to, at any moment, easily find and update the styles associated with a specific section. Let's pull the movie-index
code into another file.
/* application.scss */
@import 'base';
@import 'skeleton';
@import 'sections/movie_index';
/* assets/stylesheets/sections/_movie-index.scss */
$custom-border: 3px dotted $mustard;
.movie-index {
h1 {
color: $darker-gray;
}
.movie-show-link {
text: {
align: center;
decoration: line-through;
}
}
p {
border: $custom-border;
}
}
We've got our base skeleton's styles extracted. We've got an organized space for each section of our site's extracted styles. What about the styles that are still being shared among our site?
Remember, we want our SCSS to be as DRY as possible.
Consider the following possibly global components you'd have across your site:
- Buttons
- Navbars
- Hamburger menus
Creating a separate SCSS partial for each of these makes our code immensely more modular. So modular, in fact, that you could reuse these partials among other sites you build with a simple drag/drop and aligning of element/class names.
This cascade of partials may not be immediately implementable for your site. You may even think of another way of organizing your styles. However, with the above, you'll have:
- Skeletal styles extracted into one place
- Section partials with styles specific to that section
- Global components defined in one place
- Let's take a small example from
movie_mania
to demonstrate how to use@extend
. We want both ourh2
's on all pages andp
tag on themovie-index
to both be$maroon
. Using@extend
is similar in Sass as it is in Ruby classes. It allows the tag that holds the@extend
rule to inherit the rules of the class/id/basic selectors.
$custom-border: 3px dotted $mustard;
.movie-index {
h1 {
color: $darker-gray;
}
.movie-show-link {
text: {
align: center;
decoration: line-through;
}
}
p {
border: $custom-border;
@extend h2;
}
}
- Now our
p
inherits the same$maroon
color as theh2
tags and we don't have to repeat ourselves. But what else did this do? What changes do we see? - Fun Fact: A selector can use more than one extend!
- Mixins allow you to define styles that can be re-used throughout the stylesheet. Think about this similarly to a function or method that is reusable.
- Let's say that we want to reuse this piece of code that right now applies to all
h1
tags:
font: {
family: $fun-font;
size: 50px;
}
color: $indigo-header;
background-color: $lighter-gray;
- Let's add a mixin to our
base.scss
file, where it will be accessible to the files below it (based on@import
statements).
/* base.scss */
@mixin big-info {
font: {
family: $fun-font;
size: 50px;
}
color: $indigo-header;
background-color: $lighter-gray;
}
A mixin is define by an @mixin
followed by the name of the mixin and {}
.
And to use it in our skeleton.scss
file or any other file, we need to use @include
in our rules:
h1 {
@include big-info;
}
h2 {
color: $maroon;
}
p {
font: {
family: papyrus;
size: 30px;
}
}
What if we want some of the information in a mixin to dynamically change? We can use arguments with our mixins to acheive this. Let's change up our h1
on our movie-index
to be a little different than the other h1
's. We want it to be 40px
, a font family of gill sans
and the color #276360
. We also need to adjust our h1
in the skeleton.scss
file.
/* base.scss */
@mixin big-info($font, $size, $color) {
font: {
family: $font;
size: $size;
}
color: $color;
background-color: $lighter-gray;
}
/* skeleton.scss */
h1 {
@include big-info($fun-font, 50px, $indigo-header);
}
h2 {
color: $maroon;
}
p {
font: {
family: papyrus;
size: 30px;
}
}
/* movie-index.scss */
$custom-border: 3px dotted $mustard;
.movie-index {
h1 {
@include big-info('gill sans', 40px, #276360)
}
.movie-show-link {
text: {
align: center;
decoration: line-through;
}
}
p {
border: $custom-border;
@extend h2;
}
}
- What does Sass stand for?
- Why is Sass a better option than CSS?
- How is a variable declared and assigned in Sass?
- Why is nesting a benefit? Both at an element and property level?
- How is a mixin declared and assigned in Sass?
- Read the Sass docs :D
- Create separate stylesheets for typography (all things related to fonts on your page), colors, etc.
- Save common settings like
border
,margin
, andline-height
to variables in base.scss - Read the Sass docs :D
- Use the built in Sass methods such as
lighten
.