Skip to content

Latest commit

 

History

History
1579 lines (1254 loc) · 29 KB

17. SASS Introduction.md

File metadata and controls

1579 lines (1254 loc) · 29 KB

SASS Introduction

In this file, you will learn about:

  • What is SASS & Installation
  • Working with SASS & SCSS
  • SASS Features
  • Mathematic in SASS
  • Media Queries in SASS
  • Inheritance in SASS
  • Mixins
  • Pseudo Classes in SASS

Sass is a superset of CSS and built on top of CSS. Sass stands for Syntactically Awesome Style Sheets. It does not run in the browser, instead it extends CSS during development only.

In SASS, we can use logical styles and then compile that style to normal CSS. Because browser does not support SASS and we can use it directly and we should use it only in development.

To work with tool, we need to download this app in https://sass-lang.com/install website.

Note: You can also use scout-app open source application to use it (in https://scout-app.io/ link).

Important: If you have Node.js app, you can use npm install -g sass (for MAC, you need to add sudo) command to install this tool.

We can see the version of SASS with sass --version command.

Working with SASS

Consider this example:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Styling</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <div class="container">
        <div class="box"></div>
    </div>
</body>

</html>

and

:root {
  --black-box-border: black;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

.container {
  margin: 160px auto;
  width: 300px;
  height: 300px;
  border: 1px var(--black-box-border, black) solid;
}

.box {
  width: 200px;
  height: 100%;
  background-color: var(--black-box-border, black);
  animation: hamid 800ms steps(1000, start) infinite;
}

@keyframes hamid {
  0% {
    transform: translateX(-400px);
  }

  50% {
    transform: translateX(0);
  }

  100% {
    transform: translateX(800px);
  }
}

We want transfer this style from normal CSS to SASS (Syntactically Awesome Style Sheets), to do that, we need to add a new file and name it style.sass and paste our styles into that file.

The syntax of sass files is like:

// style.sass
:root
--black-box-border: black

*
    box-sizing: border-box

body
    margin: 0
    padding: 0

.container
    margin: 160px auto
    width: 300px
    height: 300px
    border: 1px var(--black-box-border, black) solid

.box
    width: 200px
    height: 100%
    background-color: var(--black-box-border, black)
    animation: hamid 800ms steps(1000, start) infinite

@keyframes hamid
    0%
        transform: translateX(-400px)

    50%
        transform: translateX(0)

    100%
        transform: translateX(800px)

We removed all { } curly brace pairs and ; semi-colons in here. Because the syntax of sass is like that and how it understands our styles.

Important: Indention is very important, it's like Python language which means that the box-sizing: border-box rule belongs to the * universal selector.

Note: If your editor not support the color, make sure you install the sass extension in Vs Code.

This syntax is like pug template engine and maybe someone don't like this syntax. So the greater way of using it, that is using scss file extention, for example:

// style.scss
:root {
  --black-box-border: black;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

.container {
  margin: 160px auto;
  width: 300px;
  height: 300px;
  border: 1px var(--black-box-border, black) solid;
}

.box {
  width: 200px;
  height: 100%;
  background-color: var(--black-box-border, black);
  animation: hamid 800ms steps(1000, start) infinite;
}

@keyframes hamid {
  0% {
    transform: translateX(-400px);
  }

  50% {
    transform: translateX(0);
  }

  100% {
    transform: translateX(800px);
  }
}

This style is great, because it looks like a normal CSS and indeed we can add more features to this style.

SASS Features

All files with scss we want work with them, let's describe the first feature in the SASS.

Nested Selectors

In SASS, we can have some nesting selectors, for example if we have this content:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Styling</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <header class="header">
        <nav class="main-nav">
            <ul>
                <li><a href="">Test link</a></li>
            </ul>
        </nav>
    </header>
</body>

</html>

Instead of writing styles like this:

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

.header {
  background-color: #ccc;
  height: 50px;
}

.main-nav {
  padding-top: 2px;
  height: 100%;
  text-align: center;
}

ul {
  list-style: none;
}

a {
  text-decoration: none;
}

we can use nested selectors in SASS (i.e. style.scss file) like:

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

.header {
  background-color: #ccc;
  height: 50px;

  .main-nav {
    padding-top: 2px;
    height: 100%;
    text-align: center;

    ul {
      list-style: none;

      a {
        text-decoration: none;
      }
    }
  }
}

and it's very essy to understand. We can also write in different way like:

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

.header {
  background-color: #ccc;
  height: 50px;

  .main-nav {
    padding-top: 2px;
    height: 100%;
    text-align: center;
  }

  ul {
    list-style: none;
  }

  a {
    text-decoration: none;
  }
}

because ul and a selectors are sill part of .main-nav and it's no matter they should be nested or not.

We are applied our styles in there, now we need to compile our SASS code to normal CSS. To do that, we need to go to the terminal or command line and write:

sass target output

for example, sass style.scss style.css command we want to use. The output of this compilation is:

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

.header {
  background-color: #ccc;
  height: 50px;
}
.header .main-nav {
  padding-top: 2px;
  height: 100%;
  text-align: center;
}
.header ul {
  list-style: none;
}
.header a {
  text-decoration: none;
}

/*# sourceMappingURL=style.css.map */

There is another file that named style.css.map which we cover it later.

Note: If we don't want to compile our code manually, we can compile our styles automatically with this command:

sass --watch target:output

for example, sass --watch style.scss:style.css command we can use for automate compilations. Now when we change any style and save the file (i.e. style.scss file), the style.css file would be updated automatically.

Nested Properties

If we have this content with style:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Styling</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <div class="container">
        <div class="box"></div>
    </div>
</body>

</html>
* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

.container {
  margin: 160px auto;
  width: 600px;
  height: 200px;
  border: 1px black solid;
}

.box {
  width: 200px;
  height: 100%;
  background-color: red;
  animation-name: hamid;
  animation-duration: 1s;
  animation-timing-function: ease-in;
  /* animation-timing-function: cubic-bezier(0.68, -0.6, 0.32, 1.6); */
  /* animation-timing-function: linear; */
  animation-delay: 1s;
  animation-iteration-count: 5;
  animation-direction: alternate;
  animation-fill-mode: forwards;
  animation-play-state: running;
}

@keyframes hamid {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX(400px);
  }
}

Instead of having the same property name, we can nesting them, for example we want to nesting the animation property; so we can do that with {} curly braces:

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

.container {
  margin: 160px auto;
  width: 600px;
  height: 200px;
  border: 1px black solid;
}

.box {
  width: 200px;
  height: 100%;
  background-color: red;
  // animation-name: hamid;
  // animation-duration: 1s;
  // animation-timing-function: ease-in;
  // animation-delay: 1s;
  // animation-iteration-count: 5;
  // animation-direction: alternate;
  // animation-fill-mode: forwards;
  // animation-play-state: running;
  animation: {
    name: hamid;
    duration: 1s;
    timing-function: ease-in;
    iteration-count: 5;
    direction: alternate;
    fill-mode: forwards;
    play-state: running;
  }
}

@keyframes hamid {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX(400px);
  }
}

Note: It is also useful for flexbox, grid, transform and transition.

Defining Variables

In the previous file, we worked with CSS variables, and it's also available for SCSS. The benefit of using variables in SCSS, is that it compiles those variables which does not use CSS variables (i.e. var(value, fallback)), so it will run in all browsers, even the older versions; and it uses the real value.

We define a variable with $ dollar sign (always should be a first character) and the name of the variable. For example:

$black-color: black;

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

.container {
  margin: 160px auto;
  width: 600px;
  height: 200px;
  border: 1px $black-color solid;
}

.box {
  width: 200px;
  height: 100%;
  background-color: $black-color;
  animation: {
    name: hamid;
    duration: 1s;
    timing-function: ease-in;
    iteration-count: 5;
    direction: alternate;
    fill-mode: forwards;
    play-state: running;
  }
}

@keyframes hamid {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX(400px);
  }
}

If we look at our CSS code, we see that we have this style:

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

.container {
  margin: 160px auto;
  width: 600px;
  height: 200px;
  border: 1px black solid;
}

.box {
  width: 200px;
  height: 100%;
  background-color: black;
  animation-name: hamid;
  animation-duration: 1s;
  animation-timing-function: ease-in;
  animation-iteration-count: 5;
  animation-direction: alternate;
  animation-fill-mode: forwards;
  animation-play-state: running;
}

@keyframes hamid {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(400px);
  }
}

/*# sourceMappingURL=style.css.map */

We don't have variable in this file, instead we have the real value of variable in this file. For example background-color: black in .box and border: 1px black solid in .container selectors are the real value or black as value.

Lists and Maps

We can hold more values in our variables, for example:

$black-color: black;
$simple-border: 1px solid green;

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

.container {
  margin: 160px auto;
  width: 600px;
  height: 200px;
  border: $simple-border;
}

These 1px solid green values now named as list value which means one variable with multiple values. If we want use more structured variables or group our variables in one thing, we can use map instead.

A map constructed by adding () parentheses and this parentheses we define our key-value pairs, for example:

$colors: (dark: black, light: white, normal: #ccc);

We have three key-value pairs above and we can access them by get-map(variable, key) function which is provided by SASS, for example:

$colors: (dark: black, light: white, normal: #ccc);
$simple-border: 1px solid green;

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

.container {
  margin: 160px auto;
  width: 600px;
  height: 200px;
  border: $simple-border;
}

.box {
  width: 200px;
  height: 100%;
  background-color: map-get($map: $colors, $key: normal);
  animation: {
    name: hamid;
    duration: 1s;
    timing-function: ease-in;
    iteration-count: 5;
    direction: alternate;
    fill-mode: forwards;
    play-state: running;
  }
}

@keyframes hamid {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX(400px);
  }
}

We used background-color: map-get($map: $colors, $key: normal) in the .box selector.

Note1: We just worked with one property, the map will shines when we want to work with multiple values.

Note2: We can also use background-color: map-get($colors, normal); shorter arguments.

Built-in Functions

SASS has many built-in functions, for example one them is lighten() function which is useful for lightning our colors, for example:

$colors: (
  dark: black,
  light: white,
  normal: #ccc
);
$simple-border: 1px solid green;

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

.container {
  margin: 160px auto;
  width: 600px;
  height: 200px;
  border: $simple-border;
}

.box {
  width: 200px;
  height: 100%;
  background-color: lighten(map-get($colors, dark), 50%);
  animation: {
    name: hamid;
    duration: 1s;
    timing-function: ease-in;
    iteration-count: 5;
    direction: alternate;
    fill-mode: forwards;
    play-state: running;
  }
}

@keyframes hamid {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX(400px);
  }
}

We choose dark (i.e. black) color in there. When we use 50% amount of lighening, our color would be converted to the gray color.

We can also use darken(), whiteness() and other functions.

Note: Learn more about built-in functions in https://sass-lang.com/documentation/modules.

Mathematic in SASS

We can use addition, subtraction, multiplication and division in SAS. For example if we have some styles like this:

$colors: (
  dark: black,
  light: white,
  normal: #ccc
);
$simple-border: 1px solid green;

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

.container {
  margin: 160px auto;
  width: 600px;
  height: 200px;
  border: $simple-border;
}

.box {
  width: 200px;
  height: 100%;
  background-color: lighten(map-get($colors, dark), 50%);
  animation: {
    name: hamid;
    duration: 1s;
    timing-function: ease-in;
    iteration-count: 5;
    direction: alternate;
    fill-mode: forwards;
    play-state: running;
  }
}

@keyframes hamid {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX(400px);
  }
}

We can write better styles with arithmetics:

$colors: (
  dark: black,
  light: white,
  normal: #ccc
);
$simple-border: 1px solid green;
$main-size: 100px;

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

.container {
  margin: $main-size * 1.6 auto;
  width: $main-size * 6;
  height: $main-size * 2;
  border: $simple-border;
}

.box {
  width: $main-size * 2;
  height: 100%;
  background-color: lighten(map-get($colors, dark), 50%);
  animation: {
    name: hamid;
    duration: 1s;
    timing-function: ease-in;
    iteration-count: 5;
    direction: alternate;
    fill-mode: forwards;
    play-state: running;
  }
}

@keyframes hamid {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX($main-size * 4);
  }
}

Importing Styles

If we want to import some styles from other files like:

/* test.css - export */
* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}
// style.scss - import
@import url(test.css);

If we see the Network tab in browser developer tools, we see that there are three requests; index.html, style.scss and test.css files.

We can improve the website/web app performance with partials. Partial is way of importing in SASS; to do that, we need to add a new file which is named _var or whatever we want to naming:

// style.scss - import
@import "./_var.scss";

.container {
  margin: $main-size * 1.6 auto;
  width: $main-size * 6;
  height: $main-size * 2;
  border: $simple-border;
}

@keyframes hamid {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX($main-size * 4);
  }
}
// _var.scss - export
$colors: (
  dark: black,
  light: white,
  normal: #ccc
);
$simple-border: 1px solid green;
$main-size: 100px;

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

.box {
  width: $main-size * 2;
  height: 100%;
  background-color: lighten(map-get($colors, dark), 50%);
  animation: {
    name: hamid;
    duration: 1s;
    timing-function: ease-in;
    iteration-count: 5;
    direction: alternate;
    fill-mode: forwards;
    play-state: running;
  }
}

Note: We can also use @import "var";, because SASS can understands files with _ prefix and scss extension.

Now if we go to the Network tab in browser developer tools, we see that there are just two requests; index.html, style.scss and it means our users download less data when they are visiting our page.

Media Queries in SASS

We can have media queries like:

@media (max-width: 300px) {
  body {
    background-color: yellow;
  }
}

But we can write more easier with nesting this keyword, for example:

$colors: (
  dark: black,
  light: white,
  normal: #ccc
);
$simple-border: 1px solid green;
$main-size: 100px;

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;

  @media (max-width: 300px) {
    background-color: yellow;
  }
}

.container {
  margin: $main-size * 1.6 auto;
  width: $main-size * 6;
  height: $main-size * 2;
  border: $simple-border;
}

.box {
  width: $main-size * 2;
  height: 100%;
  background-color: lighten(map-get($colors, dark), 50%);
  animation: {
    name: hamid;
    duration: 1s;
    timing-function: ease-in;
    iteration-count: 5;
    direction: alternate;
    fill-mode: forwards;
    play-state: running;
  }
}

@keyframes hamid {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX($main-size * 4);
  }
}

We wrote the media query inside of the body selector:

body {
  margin: 0;
  padding: 0;

  @media (max-width: 300px) {
    background-color: yellow;
  }
}

That's a great feature that we can use for our websites or web applications.

Inheritance in SASS

Consider this HTML content

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Styling</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <section class="main-section">
        <div class="first-element"></div>
        <div class="second-element"></div>
        <div class="third-element"></div>
    </section>
</body>

</html>

We can style this content with some methods, for example:

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}


.first-element {
  width: 300px;
  height: 200px;
  background-color: green;
}

If we want to apply this properties to another selector, we have three more ways to do that, the first way is copy and pasting the same properties like:

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;

  @media (max-width: 300px) {
    background-color: yellow;
  }
}


.first-element {
  width: 300px;
  height: 200px;
  background-color: green;
}

.second-element {
  width: 300px;
  height: 200px;
  background-color: green;
}

.third-element {
  width: 300px;
  height: 200px;
  background-color: green;
}

The second way is better, we can write those selectors in one rule:

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;

  @media (max-width: 300px) {
    background-color: yellow;
  }
}

.first-element,
.second-element,
.third-element {
  width: 300px;
  height: 200px;
  background-color: green;
}

The third way is that we can choose first-element for all elements and that's not recommended by us.

But with these three ways, we can't control the other values, for example we want to keep those elements' size and change their colors. To do that, we use inheritance; for that, we use @extend keyword which is provided by the SASS:

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;

  @media (max-width: 300px) {
    background-color: yellow;
  }
}

.main-style {
  width: 300px;
  height: 200px;
}

.first-element {
  @extend .main-style;
  background-color: red;
}

.second-element {
  @extend .main-style;
  background-color: green;
}

.third-element {
  @extend .main-style;
  background-color: blue;
}

We defined a .main-style selector which is not exist in our HTML code. Then we put the width and height into the .main-style selector.

Thereafter, all our three elements inherited the width and height from the .main-style selector, which means that all those elements have width: 300px; height: 200px; properties. Now we can easily change or add another properties to these selectors/elements.

Mixins

If we want to style of web page for both modern and older browsers, we need to reuse some properties, for example:

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

.main-section {
  display: -ms-flexbox;
  display: -webkit-flex;
  display: -webkit-box;
  display: flex;
  justify-content: center;
  align-items: center;
}

.main-style {
  width: 300px;
  height: 200px;
}

.first-element {
  @extend .main-style;
  background-color: red;
}

.second-element {
  @extend .main-style;
  background-color: green;
}

.third-element {
  @extend .main-style;
  background-color: blue;
}

We used four display properties with different values. We can mix them up in one thing with mixin. To do that, we need to use @mixin directive/keyword and write our mixin like a normal function in programming languages:

@mixin display-test {
  display: -ms-flexbox;
  display: -webkit-flex;
  display: -webkit-box;
  display: flex;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

.main-section {
  // display: flex;
  // display: -ms-flexbox;
  // display: -webkit-flex;
  // display: -webkit-box;
  @include display-test();
  justify-content: center;
  align-items: center;
}

.main-style {
  width: 300px;
  height: 200px;
}

.first-element {
  @extend .main-style;
  background-color: red;
}

.second-element {
  @extend .main-style;
  background-color: green;
}

.third-element {
  @extend .main-style;
  background-color: blue;
}

We used @include keyword for applying that mixin. There is one problem in here, if we want to use media queries in mixins, we we get an error and we need to use a tricky way to solve that issue. For example:

@mixin display-test {
  display: -ms-flexbox;
  display: -webkit-flex;
  display: -webkit-box;
  display: flex;
}

@mixin mq {
  @media (max-width: 300px) {
    background-color: yellow;
  }
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
  @include mq();
}

.main-section {
  @include display-test();
  justify-content: center;
  align-items: center;
}

.main-style {
  width: 300px;
  height: 200px;
}

.first-element {
  @extend .main-style;
  background-color: red;
}

.second-element {
  @extend .main-style;
  background-color: green;
}

.third-element {
  @extend .main-style;
  background-color: blue;
}

We don't have this issue in this example, because we used media query just for one time. If we have 3 or 4 times, their value must be changed and we don't want to all of them have max-width: 300px as screen.

We can add () parentheses after the mixin name and put parameters in there, for example we want have one more media query, so we need to write:

@mixin display-test {
  display: -ms-flexbox;
  display: -webkit-flex;
  display: -webkit-box;
  display: flex;
}

@mixin mq($width) {
  @media (max-width: $width) {
   border-radius: 50%;
  }
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
  @include mq(300px);
}

.main-section {
  @include display-test();
  justify-content: center;
  align-items: center;
}

.main-style {
  @include mq(400px);
  width: 300px;
  height: 200px;
}

.first-element {
  @extend .main-style;
  background-color: red;
}

.second-element {
  @extend .main-style;
  background-color: green;
}

.third-element {
  @extend .main-style;
  background-color: blue;
}

We used variable in function's parameter and use that function in two places (body and .main-style selectors).

We can have dynamic content in our media query with @content keyword. That would be our content in our media query, for example:

@mixin display-test {
  display: -ms-flexbox;
  display: -webkit-flex;
  display: -webkit-box;
  display: flex;
}

@mixin mq($width) {
  @media (max-width: $width) {
    @content;
  }
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
  @include mq(300px) {
    background-color: yellow;
  }
}

.main-section {
  @include display-test();
  justify-content: center;
  align-items: center;
}

.main-style {
  width: 300px;
  height: 200px;
}

.first-element {
  @extend .main-style;
  @include mq(400px) {
    order: 3;
  }
  background-color: red;
}

.second-element {
  @extend .main-style;
  @include mq(400px) {
    order: 2;
  }
  background-color: green;
}

.third-element {
  @extend .main-style;
  @include mq(400px) {
    order: 1;
  }
  background-color: blue;
}

As you can see, we have fully dynamic values in there.

Pseudo Classes in SASS

Consider this example:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Styling</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <form action="">
        <input type="text">
        <input type="password">
        <button type="submit">Send</button>
    </form>
</body>

</html>
* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

form {
  button {
    outline: none;
    border-radius: 10px;
  }
}

If we want to use hover and active pseuso classes in button like this:

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

form {
  button {
    outline: none;
    border-radius: 10px;

    :hover,
    :active {
      background-color: lightcoral;
    }
  }
}

and when we want to hover on button, we can't see anything, because the output of CSS is:

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

form button {
  outline: none;
  border-radius: 10px;
}
form button :hover,
form button :active {
  background-color: lightcoral;
}

/*# sourceMappingURL=style.css.map */

As you can see, they are part of combination of form button. To avoid that happes we can add & ampersand operator to tell the CSS these selectors must would be add to button directly:

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

form {
  button {
    outline: none;
    border-radius: 10px;

    &:hover,
    &:active {
      background-color: lightcoral;
    }
  }
}

Wow, it just works very well, we do it finally. The output of CSS now is:

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
}

form button {
  outline: none;
  border-radius: 10px;
}
form button:hover, form button:active {
  background-color: lightcoral;
}

/*# sourceMappingURL=style.css.map */

and we see that form button:hover, form button:active put in currect place.

Note: You can dive deeper in SASS with this https://sass-lang.com/guide link.