Sassをちゃんと書けるようになるためのTips

これまでSassの便利な部分を全く使えてなかったと反省しているこの頃です…
紹介する内容が「まだ使ったことない・知らなかった」ことがあれば嬉しいです。

:::note info
【この記事はこんな方にオススメ！】
・Sassをもっと使いこなしたい！
・Sassを利用しているけどmixinや変数や関数などが活用できていない
:::

CSSだとできない高度なことがあるのでガンガン利用していきましょう！
私も引き続き精進します！

## Sassってなに？
CSSを拡張したもので、繰り返し処理やネスト記法など冗長的にしか書けなかった記述が見やすく簡潔に変えるようになったスタイルシートのことです。

ファイルの拡張子は`.scss`でwebpackやgulpでコンパイルすることでcssファイルに変換されます。

```css:ネストして書ける例：CSS
.hoge {
  display: block;
}

.hoge-hoge {
  display: inline-block;
}
```

```scss:ネストして書ける例：Sass
.hoge {
  display: block;

  &-hoge {
    display: inline-block;
  }
}
```

:::note warn
SCSS記法がよく利用されますが、SASS記法も存在します。
SCSS記法の方がCSS記法に近いためSCSS記法が普及しています。
:::

## SCSSの便利な使い方
細かい設定には今回触れません。
具体的な記述にフォーカスして伝えたいと思います。

### At-Rules（アットルール）
`@`から始まる記法はアットルールと呼ばれています。
うまく使うことで効率的にSCSSを記述でき、運用や作業効率の両方でメリットがあります。

#### $string(変数）
変数に定義しておくことで何度も同じプロパティーを記述しなくて良いのとカラーやマージンのルールが一元管理できるところが変数で定義しておくことの良いところだと思います。

変数の定義方法は3種類あります。
個人的にはまとめて定義しておく方が管理しやすいと考えています。
`@each`でループを回して処理する時にも便利です。

```sass:定義
// 単一定義
$white: #fff;
$link: #1967d2;

// 配列のように定義
$colors2: white, link;

// オブジェクトのように定義
$color: (
  white: #fff,
  link: #1967d2,
);
```

変数に文字列を入れて使うこともできます。
定義した変数を`#{}`で囲むことで文字列として扱えます。

```scss
$selector = 'hoge';

.#{$selector}-text {
  color: #fff;
}

// コンパイル
.hoge-text {
  color: #fff;
}
```

#### @function
関数を定義することでJavaScriptのような計算や受け取った引数によって値を変えられます。
関数の定義方法と`map-get()`を紹介します。

```scss:利用例
// 単一で定義した変数を利用する
body {
 color: $white;
}

// オブジェクトのように定義した変数を利用する
// 関数を定義します
@function colors($key) {
  @return map-get($colors, $key);
}

// 関数を値の定義カ所で呼び出す
body {
 color: colors(wihte);
}
```

`map-get()`を利用して定義した変数から値を参照します。

1. 上記の例だと`colors()`に`wihte`を引数でわたします
1. 渡された引数は$keyに入ります
1. 定義した`colors`の中から`$key`（引数）で指定された値を取得します

#### @each
変数に定義された要素の数だけループで回ります。

```scss
.text {
  @each $modifier, $color in $colors {
    &-#{$modifier} {
      color: $color;
    }
  }
}

// コンパイル
.text-white {
  color: #fff;
}

.text-link {
  color: #1967d2;
}
```

`$modifier`と`$color`と定義して変数のkeyと値が入るようにします。
任意の文字列なので命名は`$a`や`$b`などでも問題ありません。

:::note warn
わかりやすい名前にした方が後から見た時にわかりやすいと思うので推測しやすい文字列にするのがオススメです！
:::

配列のように定義した変数も同じように`@each`で出力できます。
違う値を設定することはできないので連続して同じ処理をさせたい時に利用するのが良いと考えています。

```scss
.text {
  @each $modifier in $colors2 {
    &-#{$modifier} {
      color: #fff;
    }
  }
}

// コンパイル
.text-white {
  color: #fff;
}

.text-link {
  color: #fff;
}
```

:::note warn
利用事例を思いつきませんでした。
便利な使い方があったら教えてもらえると嬉しいです…。
:::

繰り返し処理をeachで記述しましたがforも使えます。
```scss
@for $i from 1 through 3 {
  .text-#{$i} {
    color: red;
  }
}

// コンパイル
.text-1 {
  color: red;
}

.text-2 {
  color: red;
}

.text-3 {
  color: red;
}
```

### @mixin
mixinは引数を受け取り、ブロックで返します。
@includeで呼び出すことで使用できます。

```scss
@function fontSizes($size) {
  @return map-get($font-sizes, $size);
}

@mixin fontSize($size: medium, $unit:rem) {
  font-size: fontSizes($size)#{$unit};
}

.text {
  @include fontSize(xsmall);
}

// コンパイル
.text {
  font-size: 13rem;
}
```

この程度であればわざわざ関数を作らなくても良いかと思いましたが、あえて関数を作りました。
`@mixin`は引数にはデフォルトの値をセットしてみました。
単位を`@unit`として持っておくことでpxやvwなどの指定を状況に応じて使い分けできます。
`$unit`は文字列として連結させています。

### @extend
すでに定義されたclassを@includeで呼び出し、同じルールセットを定義できます。

```scss
%text-base {
  font-size: 13px;
  font-weight: bold;
}

.text-link {
  @extend %text-base;
  color: #1967d2;
}

// コンパイル
.text-link {
  font-size: 13px;
  font-weight: bold;
}

.text-link {
  color: #1967d2;
}
```

`%`を利用して定義するとコンパイルされなくなります。
コンパイル後のコードを確認すると`@extend`で読み込んだ部分とそうでない部分で分かれてコンパイルされます。
例えば下記のようなコードをコンパイルしてみます。

```scss
%text-base {
  font-size: 13px;
  font-weight: bold;
}

.text-link {
  @extend %text-base;
  color: #1967d2;
}

.text-red {
  @extend %text-base;
  color: #ea4336;
}

// コンパイル
.text-red, .text-link {
  font-size: 13px;
  font-weight: bold;
}

.text-link {
  color: #1967d2;
}

.text-red {
  color: #ea4336;
}
```
`@extend`した部分はグルーピングされ、そうでない部分はそれぞれで定義されてコンパイルされます。
`@extend`を`@mixin`で書き換える別の結果になります。

```scss
@mixin text-base {
  font-size: 13px;
  font-weight: bold;
}

.text-link {
  @include text-base;
  color: #1967d2;
}

.text-red {
  @include text-base;
  color: #ea4336;
}

// コンパイル
.text-link {
  font-size: 13px;
  font-weight: bold;
  color: #1967d2;
}

.text-red {
  font-size: 13px;
  font-weight: bold;
  color: #ea4336;
}
```
`@mixin`はグルーピングせずそれぞれのclassとして定義されるのに対し、`@extend`は共通する部分と固有の部分を分けてコンパイルします。

:::note info
`@mixin`を例のようにシンプルに使うケースは少ないと思います。
ただ、コンパイル時の挙動が異なることを覚えておく良いと思います！
:::

### @error
`@error`を使うことで`@mixin`の引数に入ってくる値が間違ったものであればエラーを出せます。

```scss
$spacing-sizes: (
  medium: 20px,
  large: 24px,
  xlarge: 32px,
);

// spacing-sizeから値を取得する関数
@function spacingSize($size) {
  @return map-get($spacing-sizes, $size);
}

@mixin margin($size: medium, $property: bottom) {
  // $propertyにbottomまたは、rightではない時errorを出力する
  @if $property != bottom and $property　!= right {
    @error `Property #{$property} must either bottom or right.`
  }

  @if ($property) {
    margin-#{$property}: spacingSize($size);
  }
}
```

上記のようにすることでマージンを付与する方向に一貫性を持たせられます。
どんなに人が入れ替わってもこのルールがある限りは仕組みでマージン方向が統一できます！

:::note warn
コンテンツ間のマージンは付与する方向を統一しておいた方が良いと考えています。
そうすることでHTMLの実装にやCSSに一貫性ができ、読みやすいコードになると考えるからです。

Aは`top`と`left`で、Bは`bottom`と`right`などコンテンツによってレイアウト方法が異なるとコンテンツが消えた時に表示が崩れる可能性が高まるのもpropertyを統一しておいた方がいいと考える理由です。

ちなみに`bottom`と`right`にしている理由は、コンテンツや文字を読む方向に合わせています。
縦方向は上から下、横方向は右から左なのでmarginのpropertyの指定方向を`bottom`と`right`につけるのが自然だと思っています。
:::



## @mixinと@functionの使い分け
どちらも似たようなことができます。
mixinはfunctionよりも粒度が細かい。と覚えておくと良い考えています。
mixinは塊（ブロック）を返せるのに対して、functionは値しか返せないからです。

functionで関数を定義してmixinや各ファイルで利用するのがキレイではないかと考えています。

## まとめ
1つでもSassを記述時のヒントになったら大変嬉しいです。
例として記述したコードは単純でシンプルなものばかりで難しい実装の参考にはならないと思います。
ただ、基本は`@mixin`や`@function`、変数を定義して参照する構造は変わらないと思うので応用してもらえればと思います。

:::note info
CSSは記述の自由度が高い言語です。
長い期間運用する前提のサービスであればシンプルに見やすいコードを保てるような工夫が不可欠になってくると考えます！
:::

関わる全員のスキルセットに偏りが出たとしても生産性が落ちない設計を考え設計できるのが個人的なベストだと思っています。

CSS設計については下記のページに考えをまとめておきましたので興味がある方はご覧ください。

https://qiita.com/tanimoto-hikari/items/8a389f829bd150c3fc1c