SMACSSにおけるベストプラクティス。設計やモジュール定義のポイント

CSSの実装で苦しんだ経験をお持ちの方も多いと思います。
私も**会社に入ってから数えきれないぐらい悩んできました**。

CSS設計の1つSMACSSにおける設計について私の知見を伝えられたらと思います。

この記事では下記のことについて書きます。

- SMACSSで設計する時の考え方やポイント
- モジュールを定義する時のポイント

**これからCSS設計にチャレンジする方や現在の設計に課題を感じている方にとって参考になるような記事になれば幸いです。**

## SMACSSで設計する時の考え方やポイント

SMACSSについては知りたい方は公式サイトもしくは「SMACSS」で検索すると概要についてまとめられた記事があるでそちらをご覧ください。

http://smacss.com/ja

SMACSSは5つのカテゴリー（ディレクトリ）に分けてCSSを定義します。

- ベース
- レイアウト
- モジュール
- ステート
- テーマ

公式では「テーマ」はほとんどのサイトで使うことはない。と書かれていますが、
ダークテーマや大規模メディアでカテゴリーによって色分けしたい時のスタイルを定義するのに使うことを想定されているカテゴリーのようです。

### ステートとテーマは不要かもしれない
テーマだけでなく「ステート」も不要だと感じています。
理由は、「**モジュールの状態をステートで管理するのは運用しづらかった**」からです。

実際のコーディング作業をイメージしてください。
例えば、「要素がクリックされたときにborderが赤色になるように修正したい」とします。

#### モジュールとステートに分けた場合
```module/card.scss
.card {
  border: 1px solid #ccc;
}
```

```state/card.scss
.is-card-click {
  border: 1px solid red;
}
```

分けた場合は1ページに記述する量も少なく、ネストも発生しないのでシンプルです。
ただ、**cardモジュールだけ見てもクリックされた時の挙動はどうなるのかは全くわかりません。**

:::note info
個人的なベストプラクティス
cardはユーザーのアクションに対してどんな振る舞いをするのかもモジュールに含まれていて良いと考えています。
:::

なので下記のように記載します。

```module/card.scss

.card {
  border: 1px solid #ccc;

  &.is-click {
    border: 1px solid red;
  }
}
```

気をつけなければいけないのがネストの深さです。
ネストが深ければ深いほど可読性が下がってしまいます。
**1つ独自なルールとしてネストは3つまで**としています。

3つ以上ネストされるようであれば別のモジュールに分けることができるという考えからです。
下記のようにCSSが定義されていたとすれば、cardとbuttonモジュールに分けます。

#### ネストで4階層になる例
```card.scss
.card {
  &-inner {
    &-button {
      &-text {
      }
    }
  }
}
```

#### 深いネストを別のモジュールとして切り出す
```card.scss
.card {
  &-inner {
  }
}
```

```button.scss
&-button {
  &-label {
  }
}
```

深いネストを回避することは可読性をあげるだけでなくネームスペースを確保することにもなります。

### ネームスペースの確保
ネームスペースについても触れておきます。
ウィキペディアでは下記のように定義されています。

> 名前空間（なまえくうかん）はNamespaceの訳語で、名前の集合を分割することで衝突の可能性を低減しつつ参照を容易にする概念である。
> ウィキペディアより引用

前述したようにモジュールのネストが深くなったり、パターンが増えたりすると**命名の衝突が起こりやすく、実装より命名に時間が掛かってしまう**こともあります。

ネストが深くなってしまうの場合は
- HTML構造が複雑になっていないか
- モジュールを切り分けられないか

を確認するなどそもそもの実装を疑うようにしています。

:::note info
モジュールを定義する時はネームスペースにも着目して実装できるとより良い設計ができると思います！
:::

### コーディングルールは決めておく
CSS設計とは少し話がずれますが、**コーディングルールを決めてないと一貫性がなくなり時間の経過に伴って崩壊していきます。**

例えば
- マージンは必ずbottomかrightにしかつけてはいけない
- ネストは3つまで

などです。
マージンをtopだったり、bottomだったり要素によって調整方法を変えてしまうと、**ある要素が消えた時にその上下にあったコンテンツ間に全くスペースがなくなってしまう**ということが起こります。

一例ではありますが、話し合いの中でこれは守ろうは決めておいたほうが無難であると考えています。

## モジュールを定義する時のポイント
どこまでをモジュールとするかは意見が分れるところです。
結局は、**定義したモジュールが使いやすい＆わかりやすい状態になっているか**が全てだと思うので正解はないとした上で個人の考えを書きます。

### レイアウトかモジュールか
基本的にレイアウトには汎用的な配置のみを定義しておきたいのが私の考えです。
具体的なコードだと下記のようなコードです。

```base.scss
.l-flex {
  display: flex;
}

.l-full {
  width: 100%;
}

.l-bottom-medium {
  margin-bottom: 16px;
}
```
コンテンツ間の幅や大きさはレイアウトに定義する方が良いと考えています。
モジュールがコンテンツとして独立するにはレイアウトに関する定義がない方が実現しやすいと考えています。

では次に固有のwidth設定はレイアウトに定義すべきかモジュールに含めるべきかについてです。

```base.scss
.l-main {
  width: 800px;
}

.l-sideNavi {
  width: 200px;
}

```
**汎用的に使いまわさないモジュールはwidthなどのレイアウトに分類されるようなプロパティーを含めても良い**と考えています。

コンテンツ中や外で利用する想定であれば**widthで幅を固定すると利用カ所を限定してしまうことになる**ので使いにくいモジュールになります。

:::note info
モジュールにレイアウト要素を含めるか否かは、**汎用的に使う想定のモジュールであるか**をポイントだと考えています！
:::

### モジュールをどこまで細分化するか
CSS設計の悩ましい部分です。
下記のようなモジュールがあったとします。

```card.scss
.card {
  border: 1px solid #ccc;

  &-inner {
    padding: 8px 12px;
  }
}
```
下記のようにpaddingはコンテンツの中身に応じて変更できるようにいろんなパターンを用意したい。となったとします。

```card.scss
.card {
  &-inner {
    padding: 8px 12px;
    
    &-large {
      padding: 8px 16px;
    }

    &-xlarge {
      padding: 8px 20px;
    }
  }
}

// 下記のように上下のpaddingが可変されるパターンもほしい
/*
padding: 12px 8px;
padding: 12px 16px;
padding: 12px 20px;
*/
```

方法としては大きく2つあると考えています。

1. cardモジュールの中にパターンを定義する方法
2. innerモジュールというpaddingしか持たない汎用的なものを定義する方法

さらにinnerはcardモジュール以外でも使いたい場合どちらを選択しますか？
下記のようにcard以外でも内側のスペースを確保する場合を想定します。

```index.html
<section class="card">
  <div class="inner-small"></div>
</section>

<section class="list">
  <div class="inner-small"></div>
</section>
```

:::note info
paddingはモジュールに含めるほうが良いが結論です！
:::

理由は2つあります。

1つ目は…**cardモジュールがinnerを組み合わせないと完成しないことがモジュールだけ確認してもコードからではわからない**からです。

2つ目は…2パターンが混在してしまうことで**一貫性が失われてしまう**と考えているからです。

どうしても1px単位の細かい調整がモジュール側で必要になったとき

- innerモジュールでpaddingを調整する
- モジュール側でpaddingを定義する

の2つのパターンが存在してしまうと、他の方がコードを見た時にどっちのルールが正しいの判断できず一貫性が失われていくきっかけになります。

### 基本的にシングルクラスの方がいい
SMACSSはマルチクラスで記述するのが一般的です。
そこを**あえてシングルクラスで記述した方が良い**と考えます。

:::note alert
マルチクラスで長年運用するとパターンが増えすぎて混乱したり、
それを覚える教育コストが高まったりしました。
:::

2つぐらいならまだしも、3つ4つとなってくると大変です。

マルチクラスのほうがクラス1つのコード記述量が少なく、組み合わせでカスタマイズの自由度も高いです。

それは命名や記述方法が一貫して守られている前提であって、独自の記法、命名ルールが横行し始めると足かせになり始めます。

:::note info
シングルクラスでモジュールが実装できる状態にしておけるほうが柔軟性も高く、人員が流動したとしても開発生産性は一定を保ちやすいと考えます！
:::

## まとめ
繰り返しになりますが、人によっても考え方は違います。
運用していく中で

- 運用がもっと楽になるにはどうしたらよかったのか
- 人が流動的に入れ替わっても生産性が保たれるCSSの設計とは何か
- スピーディーなサイト改善に耐えられる汎用的な設計とは何か

私が考える**見通しが良く、保守しやすい**のってこんな設計かもな…を言語化しました。

CSSは人によって書き方も考え方がバラバラであっても問題ない言語だからこそ多人数かつ長期間運用するウェブサービスでは、どれだけ**一貫性を保ち続けられるか、保ちやすい環境を作れるか**にかかっていると思います。

CSS設計に限らずライブラリの導入などさまざまな方法を用いてCSSの一貫性が保たれることを願っております。