# Sass/Scss

Most useful

  • React recommends (opens new window) to avoid sharing same class in different components. Instead share components directly.
  • Parial Imports
  • Nesting
  • Avoid Variables. Instead use custom properties.
  • Special Functions (Use Css standards - calc(), env(), var(), min(), or max())
  • Mixins (mostly for prefixes) (use autoprefixer)

React/Vue Components - we can simply use css modules with scoped css classes.

Syntactically Awesome Stylesheets

  • Use .scss not .sass
  • Any valid css is valid scss.

Install

  1. sass-loader (7m) (BEST) - For webpack - Needs either node-sass or sass(recommended)
  2. sass (2m) - Pure js created using dart sass
  3. node-sass (4m) - wrapper around libsass (Both are maintained but deprecated)

# Comments

// Single line comment are NOT passed to css files

/* Multi-line comment is
passed to css files (not in minified) */

/*! added in minified css */

# Property shortcut

padding: {
  bottom: 0; // padding-bottom: 0
  left: 0; // padding-left: 0
}

# Special Functions

  • Css standards - calc(), env(), var(), min(), or max()
  • We can use Interpolation in it.
// url()
$foo: "path/to" ;
url("#{$foo}/image.jpg");   // url("path/to/image.jpg")
url($foo + "/image.jpg");   // url("path/to/image.jpg")

// calc()
$width : 500px;
calc(50% - #{$width / 2});  // calc(50% - 900px);

// max()/min()
$padding: 12px;
max(#{$padding});           // max(12px)
max($padding, 20px);        // 20px

# Variables

  • In Css - Custom Properties - JavaScript has access
  • In Sass - Variables - JavaScript has no access since it's compiled to normal css values
// --foo - Css Custom property
// $foo - sass variable
$r: red;
:root {
  --foo: $r; // --foo: $r (sass has no effect)
  --foo: #{$r}; // --foo: red
}
body {
  color: var(--foo);
}

// underscore == hyphen
$primary-color === $primary_color;

// Private Members - (variable, mixins, funtions) - can be used in same module only
// start with underscore/hyphen (both are same too)
$-color === $_color

# Css vs Sass variables

// Scope - Local/Global
// -- css var - Hoisting (limited to same/inner scopes only. Never outer scope.)
// -- sass var - No Hoisting
// ------ use `!global` flag to make it global scope from local scope

// sass ex
$a: red;
.foo {
  background-color: $a; // red
  $a: green;
  color: $a; // green
}
.bar {
  color: $a; // red
}

// css ex
:root {
  --a: red;
}
.foo {
  background-color: var(--a); /*green*/
  --a: green;
  color: var(--a); /*green*/
}
.bar {
  color: var(--a); /*red*/
}

# Interpolation

// INTERPOLATION - Mostly useful in strings (not much needed)
$name: "alert-success";
$bor: "border";
// .alert-success
.#{$name} {
  #{$bor}-color: red;
}

# Nesting

// Too much nesting is bad practice
nav {
  ul {
    margin: 0;
  }
  li {
    color: blue;
  }
}

// & - parent selector
.foo {
  &:hover {
    // .foo:hover{ }
  }
  :hover {
    // .foo :hover{ } - space is added before :
  }

  // BEM can be used but only if & = class,id,tag
  &__bar {
    // .foo__bar
  }
  &--bar {
    // .foo--bar
  }
}

# @use - Modules

  • Use @use - since @import is deprecated
  • Module is a .scss file
  • Loading Members - Variables, Functions, Mixins from other modules.
    • Added only once irrespective of how many times its called.
@use "mymodule"; // loads mymodule.(scss|sass|css)

//  partials/_variables.scss
@use 'partials/variables'; // namespace=variables - eg: mixin is used as "@include variables.mymixin"
@use 'partials/variables' as foo; //  namespace=foo - eg: mixin is used as "@include foo.mymixin"
@use 'partials/variables' as *; //  no namespace - eg: mixin is used directly "@include mymixin"

// Namespace EX
// --> _mymodule.scss
@mixin rounded {
  border-radius: $radius;
}
// --> index.scss
@use "mymodule";
.box {
  @include mymodule.rounded;
}

// Add all modules in a folder
// collection/_index.scss
@use "foo"; // collection/_foo.scss
@use "bar"; // collection/_bar.scss
// main.scss
@use "collection"; // _index.scss added automatically

Configuration (Default variable) - Not recommended

// Default (Not recommended)
// --------------------------
// _mymodule.scss
$color: red !default; // default keyword is a must
$margin: 10px !default;

// index,scss
@use 'mymodule' with (
  $color: green;  // $color is replaced but $margin is still default
);

// Mixins Maps (Better)
// --------------------------
// _mymodule.scss
$-color: red ; // no default but private variables
$-margin: 10px;
@mixin config($color:null, $margin:null){
  @if $color{
    $-color: $color !global;
  }
  @if $margin{
    $-margin: $margin !global;
  }
}
@mixin style {
  body{
    color: $-color;
  }
}
// index.scss
@use "mymodule";
@include mymodule.config(
  $color: blue;                 // $-color is changed but $-margin is still default
)
@include mymodule.style;

# @forward

// _bottom.scss
@mixin mymix {
  .foo{...}
}

// _middle.scss
@forward "bottom";
@forward "bottom" as myprefix-* ;  // we can use prefix (optional)
@include bottom.mymix; // error - no namespace
@include mymix;        // error - no mixin found

// index.scss
@use "middle";
@include middle.mymix; // works - middle.myprefix-mymix (if prefix is used)

# Mixins

  • Use Autoprefixer instead for vendor prefixes.
// EX - with any number of parameters
@mixin border-radius($radius) {
  -webkit-border-radius: $radius;
  border-radius: $radius;
}
.box {
  @include border-radius(10px);
}

// EX - without parameters (it's better to use @extend)
@mixin vars {
  .foo{...}     // can add selectors too
}
@include vars;  // can add outside {} too based on content

# @extend (BEM) & %placeholder

  • Useful for BEM Methodology
// Gives 2 classes
.myblock {
  padding: 10px;

  &__myelement {
    @extend .myblock;
    // @extend .myblock, .error, .msg; // for multiple classes
    color: red;
  }
}

// Compiled
.myblock,
.myblock__myelement {
  padding: 10px;
}
.myblock__myelement {
  color: red;
}

// But Why ?
// <div class="myblock ">               // apply - padding
// <div class="myblock__myelement ">    // apply - padding, color

# %placeholder (snippets)

  • It's a class but starts with %foo instead of .foo
// %snip will not be added to final css if it's not extended.
%snip {
  color: red;
  .foo {...}
}
.abc {
  @extend %snip; // add at top for ease
  padding: 5px;
}
.xyz {
  @extend %snip;
  border: 2px solid red;
}

# Operators

// operators allowed ->   + - * / %
div p {
  width: 600px / 960px * 100%;
}

# @ in Css Standards (TODO)

@namespace;
@font-face;
@counter-style;
@supports;
@media;
@keyframes;

# @if / else / each / for / while - (Avoid)

$dark-theme: true !default;
$color: #f8bbd0;

@if $dark-theme {
  $color: darken($color, 60%);
  // new vars are not allowed
}
.button {
  background-color: $color; // #750c30
}