デザインリニューアル時にBEMを導入してみた

# サイトデザインリニューアル時にBEMを導入した話

サイトデザインが徐々に秩序を失っていき、cssに`!important`が現れていく状況、嫌ですよね。
書いたスタイルが適用されず、上書きに次ぐ上書き……むやみやたらに登場する要素型セレクタ……止むを得ず現れる`!important`……
そんな状況となりつつあったのですが、サイトデザインのリニューアルに伴い、この機会にCSSの設計思想を1から見直してみました。

## 候補に上がったもの
- OOCSS
    - オブジェクト指向CSS
    - 基本的にはクラスを組み合わせて使う
    - bootstrapがこの思想を元に設計されているとか

- BEM
    - 要素を Block Element Modifierに分け、それぞれにスタイルを当てる
    - 基本的にクラスは1つになるので、あっちを変えたらこっちが死んだ、がない
    - クラス名が長くなるデメリットも

結果としてはBEMを採用することにしました。

## BEMに則った書き方
先ほども書いたように、要素をBlock, Element, Modifierに分けてそれぞれにスタイルを当てていきます。
例えば

```sample.html
<div class="category-list">
    <p class="category-list__category">カテゴリー1</p>
    <p class="category-list__category category-list__category--disabled">カテゴリー2</p>
</div>

<div class="category-list category-list--selected">
    <p class="category-list__category">カテゴリー1</p>
    <p class="category-list__category category-list__category--selected">カテゴリー2</p>
</div>
```

```sample.css
.category-list {
    margin: 10px;
    background-color: white
}

.category-list--selected {
    background-color: blue
}

.category-list__category {
    color: white
}

.category-list__category--disabled {
    color: gray
}
```

こんな感じでhtmlとcssを書きます。
パッと見だと何が何やらですが、もちろんちゃんと意味があります。
`category-list`はBlockで、ある要素のまとまりを表しています。いわゆる「親要素」です。
`category`はElementにあたります。いわゆる子要素です。必ずBlockの中に置く形になり、クラス名にも親となるBlockの名前を使います。
`selected`や`disabled`はModifierで、BlockやElementのバリエーション要素です。「ここだけ他と違うんだけど……」と言った時に使います。

Elementは必ず親となるBlockがあるので、そのBlockの名前をくっ付けたクラス名を指定します。
BlockとElementを区切る際は`__`(アンダースコア2つ)で区切り、Modifierは`--`(ハイフン2つ)で区切ります。
この区切り文字は`セパレーター`と呼ぶのですが、セパレーターは各環境でルールが統一されていれば、アンダースコア1つでもハイフン1つでもなんでもいいと思います。
個人的に、Elementを区切るときは`_`(アンダースコア1つ)、Modifierを区切るときは`__`(アンダースコア2つ)が見やすいと思ったので、それを採用しています。
単語区切りには`-`(ハイフン1つ)を使いたいので、Modifierを繋いだ時との区別がしやすい方がいいなぁと思ったのが理由でした。

## メリット
### どんな要素かが分かりやすい
BEMで記載すると「何を表す要素か」が一目で分かるようになるので、スタイル変更時にも影響範囲が分かりやすく、コードを書いた本人以外でも修正が比較的容易になります。

### 複数のスタイルが当たらないため、スタイルの変更が容易
各要素に対して一つのクラスが指定されるので、あちこちのスタイルが当たることがなく、ピンポイントで指定ができるのがメリットです。
`!important`と決別できるのもこの理由からです。
1つのBlock、Elementにcssを書いていくことになるので、スタイルがぶつからずシンプルに記載できました。

### 要素の使い回しが容易
Block単位で独立して動くので、あらかじめテンプレートとしてhtmlとcssをセットで用意しておくと、それに沿って書くだけで同じ要素を量産できたり、いいことが色々ありました。
特にサイト内で使用するボタンなどは統一することができます。らくちん。

### 要素型セレクターを駆逐できる
```sample.html
<div class="category-list">
    <p class="category-list__category-name">カテゴリー1</p>
    <p class="category-list__category-name">カテゴリー2</p>
    <p class="category-list__category-name">カテゴリー3</p>
</div>
```
「この`category-list`クラスの子要素は全て20pxだ！」と言う指定をしたい場合、

```sample.css
.category-list p {
    font-size: 20px;
}
```

この書き方はNGです。
この場合、`category-list`クラスの子要素を探すのではなく、全てのp要素を見つけた後、その親が`category-list`クラスかをチェックするため、パフォーマンス的にも嬉しくありません。
BEMであれば、下記のような指定をすることで、同じことが実現できます。

```sample.css
.category-list__category-name {
    font-size: 20px;
}
```

また、繰り返しになりますが、要素とスタイルが一致するため、パッと見で何をどうしているかが分かりやすくなります。

## デメリット
### クラス名が長くなる
Elementには必ず親Blockの名前を付けて書いていく必要があるので、子要素になるとクラス名が一気に長くなります。
こればかりはどうしようもないので、意識して短いクラス名を書いていくしかありません。

### 階層構造の変更に弱い
階層構造を変更すると言うことは親子関係が崩れるため、cssが一気に変わります。

```sample.html
<!-- 変更前 -->
<div class="category-list">
    <p class="category-list__category-name">カテゴリー1</p>
    <p class="category-list__category-name">カテゴリー2</p>
    <p class="category-list__category-name">カテゴリー3</p>

    <div class="category-list__other-element">
    </div>
</div>

<!-- 変更後 -->
<p class="category-name">カテゴリー1</p>
```

こうなった時に、いなくなった親の名前をhtml, cssから全て消していく必要があります。
新たにBlockを追加する際も同様に親の名前を付けていかなければいけないので、地味に手間がかかりました

### タイポに気付きにくい
意外とこれが盲点でした。
なぜか子要素丸ごとスタイルが当たらない、と言う現象があったのですが、親要素のクラス名が間違っていた、と言うことがあり、原因を突き止めるだけで結構時間を食いました。
そもそもタイポしなければいいだけの話ではありますが、意外と引っかかったので……

## もっと楽に書くために
ここまでだと「クラス名が長くて面倒くさいじゃん」と思われがちですが、実はsassと組み合わせるとcssが格段に書きやすくなります。
([sassをインストールする話はこちら](https://qiita.com/taiki-hanaoka/items/bf61f36b6e377ce97cad))

sassを使わないで書くと

```sample.css
.category-list {
    background-color: white;
}

.category-list__category-name {
    font-size: 20px;
}

.category-list__other-element {
    background-color: black;
}
```

ですが、sassを使うと

```sample.sass
.category-list
    background-color: white
    &__category-name
        font-size: 20px
    &__other-element
        background-color: black
```

だいぶスッキリしました。
htmlと同じように階層構造がしっかり表現できるので、可読性も上がりますし、親となるBlockのスタイルがどこにあるのか、が一発で分かります。
BEMを採用したのは、実はsassを導入していたから、と言うのが一番大きい要因でした。
さすがに全部手で書くのは骨が折れるので、BEMを採用する際は一緒にsassも導入すると幸せになれます。

## まとめ
- クラスに対するスタイルが一元化されるため、修正が容易
- 要素セレクターを使う必要がなくなるため、読み込み速度などパフォーマンスが向上
- 階層構造はできるだけ変えない
- タイポには気を付ける
- sassも一緒に導入すると幸せ

## 参考
- https://app.codegrid.net/entry/bem-basic-1
- https://qiita.com/rnakayama/items/e4f3b54e266f7e1867f9