Webサービス開発でのCSS設計を考える - 人類はCSS設計に敗北したのか？ -

AdventCalendar 初投稿です。

人類はまだ敗北していません（たぶん）

BtoBtoCのWebサービス開発を何回か行ってきた結果、FLOCSSとECSSを組み合わせたCSS設計に落ち着いたので、そのディレクトリ構成に至るまでの経緯と、実際のコード・失敗談や良かったことをまとめます。

## Webサービスのページ構成
ページ数の差はあると思いますが、おおむね以下のようなページ構成をとっているのではないでしょうか。

- サービスページ（トップページや各コンテンツ）
- マイページ
- その他のページ（利用規約、プライバシーポリシーなど）

## ページごとに適する設計を考える
サービス全体でひとつのCSS設計を選択するよりも、ページの特性ごとに共通化するor共通化しない、という方針を選択することによって、スムーズなコーディングができることに気づきました。

### サービスページ
トップページや一覧ページ、詳細ページなど、ページごとにデザインが大きく異なっている場合が多いため、ページをまたいで共通化できる部分があまりないです。また、デザイン変更がある可能性がマイページやその他ページよりも圧倒的に多く、共通化せずにページごとにCSSを書く方がメンテナンス性が高くなります。

### マイページ
マイページのコンテンツの多くは、情報の確認・編集がメインであるページが多いです（アカウント情報の確認や設定など）。デザインはページごとに統一されていることがほとんどであり、スタイルの変更がマイページ全体に渡って影響することが多いです。そのため、できるだけスタイルを共通化することによって、変更の手間を削減することができそうです。

### その他のページ
規約やガイドなど、文章を主体としているページでは、見出しや本文、リストのスタイルを共通化しましょう。ランディングページ等の１点ものであったり、スタイルの変更が頻繁に発生するものについては、なるべく共通化をせずに設計することで、メンテナンス時の他ページへの影響を抑えることができます。


## 実現のために必要なこと

### CSS設計を混在させる

共通化する部分としない部分をどう分ければ、コーディング時の混乱を避けるか、ということに焦点を当てたディレクトリ構成にしてみました。CSS設計手法は、共通化する部分にはFLOCSS、共通化しない部分にはECSSを利用しています。

PostCSSを使って、最終的にはstyle.cssファイルにまとめられるようにしています。

```
css
├── style.css
│
├── foundation
│   ├── _normalize.css
│   ├── _reset.css
│   └── foundation.css
│
├── layout
│   └── layout.css
│
└── object
    ├── component
    │   ├── _button.css
    │   ├── _dialog.css
    │   ├── .
    │   ├── .
    │   ├── .
    │   └── component.css
    │   
    ├── project
    │   ├── _articles.css
    │   ├── _comments.css
    │   ├── _header.css
    │   ├── _footer.css
    │   ├── .
    │   ├── .
    │   ├── .
    │   └── project.css
    │   
    ├── ECSS
    │   ├── services
    │   │   ├── _index.css
    │   │   ├── _view.css
    │   │   └── services.css
    │   ├── .
    │   ├── .
    │   ├── .
    │   └── ecss.css
    │   
    └── utility
        └── utility.css
```

### FLOCSS + ECSS

FLOCSSの、object/component内にECSSディレクトリを作っています。ECSSディレクトリ内はURLと１対１に対応させるようにしました。ECSS/services/index.cssは、 https://~~~/services/indexでのみ使われます。

URLと対応させることにより、MVCフレームワーク（自分の場合はCakePHPを使っています）との対応が取れるようにしました。サービスページでしか使わないスタイルはECSSディレクトリ内に書き、マイページやその他ページのスタイルはobject/project内に書いています。

実際には使っていないのでわかりませんが、Railsと組み合わせる場合やSPAなどでもそのまま使えるのではないでしょうか。

### 共通化している要素サービスページでも使う

button.cssで定義したものをindex.cssで再度定義することに意味はないので、元々button.cssで定義したクラスを設定し、調整する場合は別クラスを追加し、index.cssに記述するようにしています。

```html:index.html
<!-- 実際にはフレームワークのViewに相当するファイル -->
<button class="btn btn-primary">ボタン</button>
<button class="btn btn-primary si-btn-primary">ボタン</button>
```

```css:object/component/_button.css
.btn {
    display: inline-block;
    font-weight: 400;
    text-align: center;
    white-space: nowrap;
    vertical-align: middle;
    user-select: none;
    border: 1px solid transparent;
    padding: .375rem .75rem;
    font-size: 1rem;
    line-height: 1.5;
    border-radius: .25rem;
    transition: background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
}

.btn-primary {
    color: #fff;
    background-color: #007bff;
    border-color: #007bff;
}
```
```css:object/ECSS/services/_index.css
/* siは接頭辞です */
.si-btn-primary {
    padding: 5px 20px;
}
```

## 失敗談

### 共通化するべきかの選択をミスる

上記のディレクトリ構成に辿りつくまでは、サービスページ/マイページなどの、共通化する基準となる明確な区切りを見つけらていなかったため、ページごとに共通化するかどうかを決めていました。

そのため、共通化してobject/project内に作った方が良いものを、ECSS内に作ってしまい、全く同じ指定のクラスが大量に出来上がってしまう、ということがありました。おそらく、共通化すればコード量が2/3ぐらいにはなるはずですが共通化自体が大変なので手をつけられていません。助けて。。。

### 接頭辞のつけ方が良くない（特に同一ページの複数パターンを見たい時）

これは今でも困っていますが、ECSSの接頭辞のつけ方が良くないと後で困ります。例えば、services/viewで複数のパターンを見比べて最終的な選択をしたい場合、同じページに対して別の接頭辞を作ることになります。

自分がやってみたパターンで大失敗だったのが .si1-btn-primary、.si2-btn-primaryみたいなクラスのつけ方をしたときです。最終的にどちらのパターンを採用するにしても、`.si1-` や `.si2-`という書いた本人以外には意味がわからないクラスが残ってしまいました。

さらに最悪なのは、一部はsi1、一部はsi2を採用することになった、というパターンです（なりました）。これは気分の問題かもしれませんが、接頭辞の異なる同一ページのスタイルが、services/_view.cssに残ってしまい、非常に良くない感じになってしまいました。

```html:index.html
<section class="prefix1-block">
  <h1 class="prefix1-block__title"></h1>
  <p class="prefix1-block__description"></p>
</section>

<section class="prefix2-article">
  <h1 class="prefix2-article__title"></h1>
  <p class="prefix2-article__description"></p>
</section>
```

```css
.prefix1-block__title {
  font-size: 18px;
}

.prefix2-article__title {
  font-size: 20px;
}
```

BEMを利用している場合は、以下の方が良いかもしれません。一時的に詳細度が上がることになりますが、許容範囲だと思います。

```html:index.html
<section class="block block1">
  <h1 class="block__title"></h1>
  <p class="block__description"></p>
</section>

<section class="block block2">
  <h1 class="block__title"></h1>
  <p class="block__description"></p>
</section>
```

```css
.block1 .block__title {
  font-size: 18px;
}

.block2 .block__title {
  font-size: 20px;
}
```
両方のパターンを見比べた後、block1もしくはblock2をそれぞれ削除していくことによって、綺麗なコードになるはずです。

## 良かったこと

### ファイル移動回数が減った
FLOCSSだけを利用している場合、ページごとではなくモジュールごとにファイルを分割することが多く、特定のページでしか使われていないスタイルがarticles.css、comments.cssなど、複数ファイルにまたがって存在してしまう場合があります。

ECSSと組み合わせることにより、こういった特定ページのみのスタイルが１ファイルになるため、コーディング中のファイルの行き来が少なくなりました。

### スタイルを定義する場所に迷わなくなった
複数回出現するかどうかわからないスタイルをとりあえず、project/ECSSに入れておくようにし、３回同じものが現れたら共通化してobject/projectに移動するようにしました。（[Web制作者のためのCSS設計の教科書](https://www.amazon.co.jp/gp/product/4844336355/) 書いてあるRule of threeを参考にしています。）

全体的にコーディング速度が向上するとともに、開発時のストレスが減りました。


## まとめ

この記事を書こうと思ったきっかけは以下のツイートでした。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">CSS設計の話を聞かなくなったのは</p>&mdash; kzhrk (@kzhrk0430) <a href="https://twitter.com/kzhrk0430/status/911387349757468672?ref_src=twsrc%5Etfw">2017年9月23日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 宣伝

### 個人開発コミュニティを運営しています
イベント開催やもくもく会を主にやっています。参加&運営メンバー募集中です。

Node&Arcs（[Slackグループ]([https://join.slack.com/t/node-and-arcs/shared_invite/enQtMjYzNjM4NjgzNzgwLWJmYWE0NTI3ZjFkNTA1ZDExOWI4ZDdjM2JhNWQ2YTM4NjFmNzI4NjViMDJhMDQ2NWIyYTFkMzA3MjZmOTI5Mzg])）
https://node-and-arcs.connpass.com/