はじめてのCSS設計の記録【決定編】

## はじめに
この記事は[はじめてのCSS設計の記録【検討編】](https://qiita.com/kum44/items/10db8b9cd6ec45303681)からの続きとなっています。
初めてCSSの設計を行った記録になります。改善できる部分はまだまだあるかと思いますので、都度振り返りを行い定期的にアップデートをしていきたいと思います。

## はじめてのCSS設計

基本設計：**PRECSS**
命名規則：**MindBEMding**

### MindBEMding(マインドベムディング)とは
BEMをCSSのクラス名に適用するために作られた規則のことです。CSSの話をしている時はMindBEMdingがBEMと呼ばれることが多いそうです。つまり、「CSSはBEMを使おう」→「CSSのクラス名はBEMのMindBEMdingに従って命名しよう」と変換することができます。

### CSSのカテゴライズ
1. ベース
2. レイアウト
3. モジュール
4. ユニーク
5. ヘルパー
6. プログラム

基本的にはPRECSSの設計をそのまま使用しています。各グループをそれぞれファイルに分けて管理しています。公式との差異は①モジュールに接頭辞をつけないこと、②モジュールをブロックとエレメントにファイル単位で分割しないことの2点です。

#### 1. ベース
配置内容：HTML要素、標準スタイル、リセットCSSなど
接頭辞：なし

ベースにはプロジェクトの標準となるスタイルや、リセットCSSを配置します。ほとんどをHTML要素に対してスタイリングしています。今回、リセットCSSにはブラウザ間の統一は図るけど要素の役割の全てを壊したくないという考えから[Normalize.css](https://github.com/necolas/normalize.css/blob/master/normalize.css)を適用しました。
更に細かく分けて管理したい場合、PRECSSではオリジナルのグループを作成することができる柔軟な設計になっているので、リセットCSSを新規のグループに分けて配置するのがいいでしょう。

#### 2. レイアウト
配置内容：ヘッダー、メインエリアなどの大きなレイアウトを形成する要素
接頭辞：ly_

レイアウトにはヘッダー、メインエリア、フッターなどの要素を配置します。メインエリアの中が2カラムであれば、更にコンテンツエリアとサイドエリアなどに分けていきます。画面ごとにレイアウトが異なる場合はその都度枠組みとなるスタイルを追加していく必要があるでしょう。
他の開発者がHTMLを見た時、`ly`の接頭辞がついているクラス名をみて、このクラスがレイアウトを形成しているから自分が今から開発しようとしている画面にも適用しよう、という風に判別がつきやすくなっています。

#### 3. モジュール
配置内容：再利用性のあるコンテンツ
接頭辞：なし

モジュールには再利用性のあるスタイルを配置します。モジュールとは、コンポーネントとも言い換えることができ、`特定のエリアに依存せずどこでも使用が可能なもの`となっています。特定のエリアに依存せず、とはモジュールが他のスタイルが適用されている要素下でもデザインの崩れがない状態を指します。
PRECSSのモジュールには`ブロックモジュール`と`エレメントモジュール`に分けられていますが、BEMを使用したいと思い今回は統一しています。
更に、接頭辞がありませんが、これは接頭辞なしのクラス名はモジュールグループと判断することができ、あまりクラス名が長くならないようにと考え、つけない方針にしました。

モジュールにはBEMの命名規則を適用します。
BEMとは`Block`、`Element`、`Modifier`の略で、それぞれ以下のように定義されています。

**Block**：どこでも使い回せるパーツ
**Element**：Blockの構成要素、Blockの中でしか機能しないもの
**Modifier**：BlockやElementの修飾を定義するもの

記法は以下のようになっており、Elementはアンダースコア2つ(__)、Modifierはハイフン2つ(--)で連結するようにします。

```
block
block__element
block__element--modifier
block--modifier
block--modifier__element
```
エレメントの下層にエレメントが来るパターンもあるかと思いますが、その場合もエレメントを2つ繋げずに、`block__element`の記法で問題ありません。ブロックが一意になっている想定なので、エレメントを重ねて命名する必要がないためです。
ElementやModifierを定義するとき必ず先頭にBlockになりクラス名が長くなりがちですが、これは`どのブロックに対してなのかがわかる`や`みだりに影響範囲を広げない`といった目的があります。BEMを適用する際、後述する`Sass`を使用することで効率よく開発することができますのでぜひ取り入れましょう。

#### 4. ユニーク
配置内容：ある特定の場所でしか使用していないスタイル
接頭辞：un_

特定の場所でしか使用していないので、影響範囲がなく後からの修正が容易です。ただ、あまり乱用すると再利用性がなくなってしまうので、ユニークに配置するときはモジュールに配置できないか一旦検討してみるのがいいと思います。
ユニークグループに配置したスタイルには、どの画面で使用しているかそれぞれコメントを残すようにします。

#### 5. ヘルパー
配置内容：単一のスタイルなど
接頭辞：hp_

marginやpaddingなど、ある部分だけを調節したいとなった場合に適用するスタイルを配置します。BootStrapの`mt-0`のようなクラスを定義するイメージです。

#### 6. プログラム
配置内容：JSなどでHTML要素を取得する際にセレクタとなるクラス、状態を管理するクラスなど
接頭辞：js_ または is_

JSで要素に対して何か操作をする際に、接頭辞の「js_」を付けたクラスをセレクタとして扱います。また、状態の管理は接頭辞「is_」を使用し、例えば`is_active`というクラスに`display: block`などを当てる、といったようにスタイルを適用します。

### 開発手順
##### 【使用するもの】
* **Fractal**：スタイルガイドジェネレーターの1つ。プロジェクト内のモジュールを一元管理することができる（[公式サイト](https://fractal.build/guide/)）
* **Sass(LibSass)**：CSSメタ言語。今回はnode-sassライブラリでSassを使用する（[公式サイト](https://sass-lang.com/libsass)）
* **gulp**：様々な処理を自動化するツール。Sassのコンパイル時に使用する（[公式サイト](https://gulpjs.com/)）

開発において使用するツールや言語です。
詳しい説明はそれぞれ公式サイトをご確認ください。

##### 【事前準備】
* **fractal.config.js**があるディレクトリで`fractal start --sync`    
  →command not foundの場合
  * `../node_module/.bin/fractal start --sync`    
  または
  * `npm i -g @frctl/fractal && fractal start --sync`
* **gulpfile.js**があるディレクトリで`gulp sass:watch`    
　→command not foundの場合
  * `node_module/.bin/gulp sass:watch`    
  または
  * `npm i -g gulp-cli && gulp sass:watch`

開発の前に2つのコマンドを入力しそれぞれ起動させておいてください。
fractalの方はfractal.config.jsを配置しているディレクトリ内のドキュメントやコンポーネントが保存された時、gulpの方はscssファイルが保存された時、それぞれ自動で更新がかかるようになっています。

##### 【手順】
1. Fractal上で利用できそうなモジュールを検索する
2. 該当するモジュールが存在する場合はそのまま使用する
3. 存在しない場合はFractalにモジュールのHTML側を作成する
4. Sassでスタイリングする
5. gulpでscssファイルをコンパイルする
6. Fractalでモジュールの確認をする
7. 実際のHTMLファイルにモジュールを適用する

#### 1. Fractal上で利用できそうなモジュールを検索する
Fractalはスタイルガイドジェネレーターの1つです。スタイルガイドとはUIパーツやプロジェクト内のテーマをまとめておき、デザインの現物をそのまま確認することできるので、他の開発者が後から見た時に自分の使うべきクラスの見通しが立ちやすくなります。

まずはこのFractal上で、自分が利用したいモジュールがないか確認します。

![image1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245419/243b4f0b-caf3-8014-4cc4-1f614a9d57d7.jpeg)


#### 2. 該当するモジュールが存在する場合はそのまま使用する
利用できそうなモジュールがあった場合は、下の`HTML`タブで適用されているクラス名を確認して実際のHTMLファイルに記述します。
例でいうと、`標準ボタン`モジュールを使用したいので、`HTML`タブを見て`btn`クラスを確認し、HTMLファイルに適用する、といった流れになります。

![image2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245419/ebd336cd-e95d-52b4-a7dd-e992db227a73.jpeg)


#### 3. 存在しない場合はFractalにモジュールのHTML側を作成する
利用できそうなモジュールがない場合は自分で新規作成する必要があります。このとき、後のこと考えてまずはFractal上でモジュールを作成していきます。

以下は、Fractalのディレクトリ構成となっています。
`styleguide`という名前でなくても問題ありません。

![image3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245419/d88e866a-2114-0f65-0267-4ace2749c9f2.jpeg)


**components**：各モジュールを配置します
**docs**：ドキュメントを配置します
**fractal.config.js**：fractalの設定ファイルです

`components`ディレクトリにはHTML側を実装していきます。初期設定では`handlebars`になっていますが、今回は`pug`を使用します。pugに変更する場合は`fractal.config.js`で設定することができます。

`components`ディレクトリには更にディレクトリを切っていますが、これはなくても問題ありません。一応大きなコンポーネントの括りで分けています。

以下の例では、`Button`のディレクトリの中に`Default`と`Outline`が存在しています。

![image5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245419/9980eb4c-b9dd-c1aa-6792-8e5eaf599f17.jpeg)


これは`styleguide`ディレクトリで`button`ディレクトリを切ったからではありません。ファイル名によってこうしたディレクトリ構成になっています。

![image4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245419/09c1157e-f449-4e57-7632-2b1f878c8ec3.jpeg)


このように、`button.pug`とModifierである`button--outline.pug`がありますが、Fractalではハイフン2つを連結させることで、そのファイルをデフォルトのモジュールに対しての変化系として認識させることができます。
ちょうどBEMのModifierと同じ命名方法だったのでそのまま使用することにしました。

長くなりましたが、最後にbutton.pugの中身を見てみましょう。

```pug:button.pug
a.btn(href='#') 標準ボタン
hr
a.btn(href='#') テキストが長い場合は折り返される標準ボタン
hr
.btn divタグの標準ボタン
```

ボタンモジュールなのでとても単純なので、複合モジュールのようなエレメントがたくさん入れ子になっているものだともっと複雑なものになるかと思います。
今回button.pugとしては1行目のコードだけで十分だったのですが、念の為Modifierまではいかないけど、そのモジュールの変化型をまとめておきたいと思い、`hr`タグの線で区切って複数パターンを表示させています。

他の開発者がFractalを使ってモジュールを見た時、このHTML要素で`.btn`クラスを使用すれば画面に表示されているボタンが使用できることを認識でき、再利用されてコードの重複を抑えることに繋がります。

#### 4. Sassでスタイリングする
先程のボタンモジュールには`.btn`クラスを当てていました。このクラスに対してSassを使用してCSSの実装を行います。

なぜSassを使用するのかというと、PRECSSの6つのグループごとにファイルを分けたかったというのが理由になります。
CSSのままファイルを分けてしまうと、全てのファイルを1つずつ読み込む必要があり、微小ながらパフォーマンスに影響が出てしまいます。
Sassはコンパイルする時に複数のscssファイルを1つのCSSファイルにまとめることができ、読み込みも1つのファイルで済むことができます。

ディレクトリ構成は以下のようになっています。

![image6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245419/aa3ef5ec-f2a1-c567-7f02-1fb32209a3f1.jpeg)


`sass`ディレクトリ直下に`style.scss`、`_utiles.scss`、`categories`ディレクトリが配置されています。
`categories`にはPRECSSの6つのグループがファイルごとに分けられています。
`style.scss`以外のファイル名の先頭にアンダースコアが付いていますが、これはコンパイル時にファイルが生成されないようにするためです。では、そのファイルの中身はどのようにCSSとして出力されるかというと、`style.scss`の中身を見ることでわかります。

```sass:style.scss
@import 'categories/base';
@import 'categories/layout';
@import 'categories/module';
@import 'categories/unique';
@import 'categories/helper';
@import 'categories/program';
```

ファイル名にアンダースコアが付いたCSSファイルを出力しないscssファイルを**パーシャルファイル**と言いますが、このパーシャルファイルを`style.scss`で全てインポートすることで内容を一つにまとめ、`style.css`として出力することができます。
この時、インポートしたファイルの順番通りにCSSの中身も出力されるので、下の方にインポートしたscssファイル程優先度が高くなります。なので、自分は上書きが起きてほしいグループ程下にインポートするようにしているので、この辺は特にオリジナルグループを作成した時は、各自調整してみてください。

またまた長くなりましたが、最後に`.btn`のスタイリングですが、今回は`_module.scss`ファイルに記述していきます。

```scss:_module.scss
.btn {
  display: inline-block;
  width: 300px;
  max-width: 100%;
  padding: 6px 10px;
  background-color:  #004E98;
  border: 2px solid transparent;
  border-radius: 5px;
  box-shadow: 0 3px 6px rgba(0, 0, 0, .16);
  color: #fff;
  font-size: 1.125rem;
  text-align: center;
  text-decoration: none;
  transition: .25s;

  &:focus,
  &:hover {
    background-color: #fff;
    border-color: currentColor;
    color: #004E98;
  }

  &--outline {
    background-color: #fff;
    border-color: #004E98;
    color: #004E98;
    &:focus,
    &:hover {
      background-color: #004E98;
      border-color: transparent;
      color: #fff;
    }
  }
}
```

#### 5. gulpでscssファイルをコンパイルする

今回はgulpの設定で、ファイルを保存するとコンパイルしてくれるようにしています。
以下は出力されたCSSです。

```css:style.css
.btn {
  display:inline-block;
  width:300px;
  max-width:100%;
  padding:6px 10px;
  background-color:#004e98;
  border:2px solid transparent;
  border-radius:5px;
  box-shadow:0 3px 6px rgba(0,0,0,.16);
  color:#fff;
  font-size:1.125rem;
  text-align:center;
  text-decoration:none;
  transition:.25s
}

.btn:focus, .btn:hover {
  background-color:#fff;
  border-color:currentColor;
  color:#004e98
}

.btn--outline {
  background-color:#fff;
  border-color:#004e98;
  color:#004e98
}

.btn--outline:focus, .btn--outline:hover {
  background-color:#004e98;
  border-color:transparent;
  color:#fff
}
```

`.btn`に対して`.btn--outline`という枠線のみのボタンを表示させるModifierを用意しました。
SassはBEMととても相性が良く、scssの記述量が少なく済んでいるのが出力結果からわかるかと思います。

スタイリングが完了しましたので、後はFractalで実際のデザインを確認し、HTMLファイルへの方へ反映させて一連の流れが完了となります。

普通にCSSを書いて実装するより、時間が多少かかるかと思いますが、管理や他の開発者への共有のことを考えると時間をかけるべき工程だと思っています。

以上となります。
ありがとうございました。

## 参考
[2018年の終わりに改めてgulpでsass(scss)をコンパイルする【決定版】](https://qiita.com/7110/items/a9ea618f9b9c2687c949)
[fractal のテンプレートをpugにする](https://qiita.com/fujihiko/items/ddc7be51796d7cea0594)
[Fractalを使ったスタイルガイドの作成（中級編）](https://parashuto.com/rriver/tools/fractal-detailed-settings)