docs/STYLES.md
# Styles
Next up we are going to add styles to our not-so-fancy-yet application. We are going to use [Sass](http://sass-lang.com/) as it's perhaps the most widely used **CSS preprocessor** (*and you should use one, as it helps you with managing your styles*).
### Initialize
First we will again add some dependencies to our project
```
yarn add -D node-sass sass-lint
```
to actually build **Sass** with [node-sass](https://github.com/sass/node-sass) and then lint it with [sass-lint](https://www.npmjs.com/package/sass-lint).
Next we'll create a `.sass-lint.yml` file to define the conventions for our **Sass**-files, you can check the available [rules here](https://github.com/sasstools/sass-lint/tree/master/docs/rules), but this is what I use
```yaml
options:
merge-default-rules: false
rules:
bem-depth:
- 4
- max-depth: 4
class-name-format:
- allow-leading-underscore: false
- convention: hyphenatedbem
declarations-before-nesting: true
extends-before-declarations: true
```
from which the first command `merge-default-rules` indicates that I do not want to use the default rules as a basis, the second defines that for [BEM naming](http://getbem.com/naming/) the maximum depth is four, the third that I don't allow class names that start with an underscore and that they must follow `hyphenatedbem`-convention, the fourth that I want style declarations to be before nested selectors and the last that possible [`@extend`](http://sass-lang.com/guide) must be before style declarations.
### Styles.scss
All of our style-sheets will live inside a folder `src/styles` and the first will be the "main"-stylesheet, called `styles.scss` (*`scss` is the **Sass** file extension*)
```scss
@import 'variables.scss';
@import 'index.scss';
@import 'button.scss';
@import 'todocomponent.scss';
@import 'loader.scss';
body {
background-color: $tertiary-color;
font-family: 'Roboto', sans-serif;
}
```
and at this point it only [imports](http://sass-lang.com/guide) our other (*soon-to-be-written stylesheets*) so that the compiler will know to bundle them all and sets two styles on our `body`, a `background-color` using the variable `$tertiary-color` (*more about variables in a bit*) and a `font-family` of `'Roboto'`, with a backup font of `sans-serif`. But wait, what is this `'Roboto'` you might ask? It is the main font of [Google's Material Design](https://material.io/) used in Android etc. and a very nice simple font. Now the way to be able to use it in our styles is to add the following line to the `head` element of our `index.html`
```html
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet" />
```
which will include the font from Google's CDN.
### Variables
Next we will define those things we call `variables` in **Sass** in their own file called `variables.scss`
```scss
// Colors
$primary-color: #343488;
$secondary-color: #5656AA;
$tertiary-color: #F0F0FF;
$modal-background: rgba(100, 100, 100, .8);
```
where the underscore in the beginning of the file name is just a convention to differentiate it from regular style files. Inside it we define four different colors (*using comments to define the variable "blocks" of colors, sizes etc. is just another convention*), a primary color, secondary color, tertiary color and a color for the background of a modal. All variables in **Sass** must begin with the dollar `$` sign.
### Index
Next up is the styles for the `Index`-page, inside a file called `index.scss`
```scss
@import 'variables.scss';
.index {
align-items: center;
display: flex;
flex-direction: column;
justify-content: space-around;
&__header {
color: $primary-color;
}
&__form {
align-items: inherit;
display: flex;
flex-direction: inherit;
justify-content: inherit;
&__label {
margin-bottom: 5px;
}
&__input {
background-color: inherit;
border: 0;
border-bottom: 1px solid $primary-color;
margin-bottom: 10px;
text-align: center;
width: 250px;
&:focus {
border-bottom: 2px solid $secondary-color;
outline: none;
}
}
}
&__todo-container {
display: flex;
flex-direction: inherit;
justify-content: flex-start;
}
}
```
where we introduce **nesting**. I will just explain the simplest use case so you understand what is happening
```scss
@import 'variables.scss';
.index {
align-items: center;
display: flex;
flex-direction: column;
justify-content: space-around;
&__header {
color: $primary-color;
}
}
```
where we see that first we have defined a block for the class `index`, which styles the [flexbox](https://developer.mozilla.org/en/docs/Web/CSS/flex) properties for it. Inside we have another block, which starts with `&`, which is a special character in **Sass** as it translates the `&` ampersand to the parent block's selector. So the above would output as CSS something like this:
```css
.index {
align-items: center;
display: flex;
flex-direction: column;
justify-content: space-around;
}
.index__header {
color: #343488;
}
```
> This shows us one the uses of **BEM** as it ties very nicely with the nesting in **Sass**. You can read more about the reasoning behind **BEM** [here](http://getbem.com/faq/#why-bem), but the main point is that **BEM** is as modular and independent as most JavaScript modules, while keeping it similar for every developer.
### Button
In a file called `button.scss` we are going to write our styles for the `Button`-component
```scss
@import 'variables.scss';
.btn {
background-color: $tertiary-color;
border: 1px solid $primary-color;
border-radius: 50%;
cursor: pointer;
}
```
which are very simple, like the component itself.
### TodoComponent
The styles for our `TodoComponent` will be set in a file called `todocomponent.scss`
```scss
@import 'variables.scss';
.todo {
display: flex;
flex-direction: row;
margin-bottom: 10px;
&__checkbox {
cursor: pointer;
}
&__number {
margin-left: 5px;
}
&__title {
margin-left: 10px;
}
}
```
which is a rather simple style as well, just a little **flexbox** in there.
### Loader
Now this is something a bit more interesting, we are going to make our `Loader`-component finally come to life, by creating the styles for it inside `loader.scss``
```scss
@import 'variables.scss';
.loader {
animation: .8s fadein .2s linear forwards;
background: $modal-background;
height: 200vh;
left: -50vw;
opacity: 0;
position: fixed;
top: -50vh;
width: 200vw;
z-index: 1000;
&__spinner:before {
animation: spinner .6s linear infinite;
border: 2px solid #cccccc;
border-radius: 50%;
border-top-color: #333333;
box-sizing: border-box;
content: '';
height: 120px;
left: 50%;
margin-left: -60px;
margin-top: -60px;
position: fixed;
top: 50%;
width: 120px;
}
}
@keyframes spinner {
to {
transform: rotate(360deg);
}
}
@keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
```
and this might use some explaining. There are two major points of interest here, the first being [CSS animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations) and the second being [the `:before` selector](https://developer.mozilla.org/en/docs/Web/CSS/::before).
---
CSS animations are built by setting a value for `animation` inside the CSS selector
```css
.selector {
animation: 1s name 2s linear forwards;
}
```
where the first variable (*optional*) is the delay for starting the animation, second is the name of the animation (*more about that in a bit*), third is the length of the animation, fourth is an [animation timing function](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timing-function) and the last one is [animation fill mode](https://developer.mozilla.org/en/docs/Web/CSS/animation-fill-mode).
Now the animation name has to match a [`@keyframes`](https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes) selector
```css
@keyframes name {
from {
opacity: 0;
}
50% {
opacity: 0.5;
}
to {
opacity: 1;
}
}
```
where you define the style for the object being styled at different points of the animation (*any CSS style is valid here*). You can either specify those styles with percentages (*such as the 50% here*) or as the `from` and `to` selectors (*`from` for the first frame and `to` for the last one*) or mix them up.
---
The `:before` selector on the other hand is used to create a child (*it must have something set to it's `content` property to show*) for the element, that is used to create some kind of styling otherwise impossible to create, for example in our case the actual spinning element.
### Scripts
Now that we have our styles set up, we need to include them in our application and to do that, we are going to update our **webpack** configurations and update our `index.tsx`-file a bit, beginning with the `index.tsx`-change, which is to add the following line as the last `import` statement:
```typescript
import './styles/styles.scss';
```
which is because **webpack** only bundles files related to the `entry`-file (*our `index.tsx`*).
---
To use **webpack** with **Sass** we need to first add a couple of new development dependencies, so go ahead and:
```
yarn add -D style-loader css-loader sass-loader extract-text-webpack-plugin
```
where [`sass-loader`](https://webpack.js.org/loaders/sass-loader/#src/components/Sidebar/Sidebar.jsx) compiles our **Sass** to **CSS**, [`css-loader`](https://webpack.js.org/loaders/css-loader/#src/components/Sidebar/Sidebar.jsx) allows **webpack** to process **CSS**, [`style-loader`](https://webpack.js.org/loaders/style-loader/#src/components/Sidebar/Sidebar.jsx) injects said **CSS** straight into the HTML (*faster for development*) and [`extract-text-webpack-plugin`](https://webpack.js.org/plugins/extract-text-webpack-plugin/#src/components/Sidebar/Sidebar.jsx) extracts our **CSS** into a single file when doing production builds.
For our development configuration we only need to add the following new rule to `webpack.dev.js`:
```javascript
// ...
module: {
rules: [
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
}
]
}
```
where we tell **webpack** to process our **Sass** through the described loaders (*from right to left*).
For our production build we need to do a few more changes into `webpack.prod.js`:
```javascript
var ExtractTextPlugin = require('extract-text-webpack-plugin');
// ...
module : {
rules: [
{
test: /\.scss$/,
use: ExtractTextPlugin.extract(['css-loader', 'sass-loader'])
}
]
},
// ...
plugins: [
new ExtractTextPlugin(path.join('styles.css'))
]
```
where at first we use `extract-text-webpack-plugin` to extract all processed **Sass** and then in `plugins` we tell it to save them in a file called `styles.css`.
### Alternatives
- [Less](http://lesscss.org/), another CSS preprocessor
- [Stylus](http://stylus-lang.com/), the "third" CSS preprocessor (*Sass and Less are older*)
- [PostCSS](http://postcss.org/), a CSS postprocessor