社内でCSSの新しい方針について話したメモ

社内で新しいドメインを設立するにあたり、__CSS Modules, PostCSS, cssnext__を試してみました。
このスライドは、その際の説明に使ったものです。せっかくなので公開します。

「プロトタイプ作成で試してみたけど、みなさんどう思いますか？」くらいの温度感。本番採用が確定したわけではありません。何かお役に立つことがアレば幸いです。

---

## 以後、説明に使ったスライド。

---

# おしながき

  - 1. コンポーネント時代のスタイリング
  - 2. グローバルCSS、BEM、そしてローカルCSS
  - 3. CSS Modules、そしてJSXへの割り振り
  - 4. cssnextと、その書き方
  - 5. 我々のPostCSSスタンダード

---

#### 新ドメインの<br>CSS環境（案）


---

#### CSS Modules <br> css next <br> PostCSS

---

#### on webpack

--- 

![68747470733a2f2f7765627061636b2e6769746875622e696f2f6173736574732f6c6f676f2e706e67.png](https://qiita-image-store.s3.amazonaws.com/0/77614/1d00b146-af85-2a83-02ba-97d8e75b86d3.png)

---

## 何が変わるのか

--- 

- 我々の今までのスタイリング
 - sassで書く
 - スタイルのモジュール化とネスト、そしてbodyクラスを駆使して、スタイルの衝突を防ぐ
 - gulpでconcatした１枚の巨大なCSSをHTMLから読み込む

--- 

- これからのスタイリング
 - （ほぼ）生のCSSを書く
 - クラス名の衝突を気にする必要はない。かつ、BEM等は使わない
 - クラス名をJavaScriptにimportして、JSXにバインドする
 - webpackでconcatした１枚のCSSをHTMLから読み込む

---

#### 順を追って説明します。

---

### 1. コンポーネント時代のスタイリング

---

<sub> ここでのコンポーネントは、CSSを単にカプセル化するという意味ではなく、Reactのコンポーネントに対応してコンポーネント化するという意味。</sub>

--- 

# 例

---

## ボタンコンポーネント

```js
// Button.jsx
const Button = ({ onChangeButton }) => (
  <div>
    <button
      onClick={onChangeButton}
    >
    変更
    </button>
  </div>
)
```

※例なのでけっこう雑です

---

## 対応するCSSコンポーネント

```css
/* Button.css */
.container {
   margin: 0 auto;
}

.container .button {
   background-color: #996;
   color: #fff;
}

.container .button:hover {
   background-color: #ff6;
}
```

※例なのでけっこう雑です

---

## 基本的にはJS(X)と<br>CSSの範囲が一致。

--- 

## メリット

- スタイルが見つけやすい
- 再利用性が高まる
- 捨てやすい

参考リンク: [コンポーネント時代の CSS 設計](http://www.slideshare.net/hoto17296/css-59425854)

---

とは言え、今までもHTMLとSCSSはパーシャル化して対応させていた。
我々にとって、コンポーネントCSSは真新しいことではないかもしれない。

---


## 2. グローバルCSS<br>BEM、<br>そしてローカルCSS

---

# CSSの問題点

---

<img width="596" alt="Screen Shot 2016-08-18 at 14.15.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/77614/ea8b259e-71cf-c29d-5320-2e573d53f3ee.png">

<sub>[CSS Modules - Welcome to the Future- ](http://glenmaddern.com/articles/css-modules)より引用</sub>


---

## 基本的には全てのCSSはグローバルである

---

## スタイルの影響範囲が分からない

---

## 「なぜかスタイルが効きません😫」

---

## かつての対応策

---

<img width="527" alt="Screen Shot 2016-08-18 at 14.17.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/77614/afa1e31d-6caf-511b-dca3-2ab4ad19d345.png">

<sub>[悩まないコーディングをしよう！ OOCSS,SMACSSを用いた、読みやすくてメンテナブルなCSS設計（Sass対応）
](http://www.slideshare.net/horiguchiseito/master-architecture-forcss)より引用</sub>

---

## 命名規則で対応していた

```html
<div class="block">
    <div class="block__element"></div>
    <div class="block__element--modifier"></div>
</div>
<div class="block--modifier">
    <div class="block--modifier__element"></div>
</div>
```

<sub>[BEMという命名規則とSass 3.3の新しい記法](http://blog.ruedap.com/2013/10/29/block-element-modifier)より引用</sub>

---

## 我々の対応

命名規則での対応は、我々が通らなかった道。
bodyクラスで対応していた。

```scss
// application.scss
body.account {
  @import "page/account.scss";
}

body.setting {
  @import "page/setting.scss";
}

```
CSSは\<body/>単位でローカル化される。

---

## ローカルCSS

---

## css-loader

webpackのcss-loaderをモジュールモードで使えば、以下のCSSが

```scss
// common.css
.button {
  // here comes style
}
```

以下のように変換される。

```scss
// application.css
.common-button-1DYla {
  // here comes style
}
```

---

## モジュールモードをON

CSS Modulesは、css-loaderのモジュールモードをtrueにした状態。

```js
// webpack.config.js
const cssLoaderQueryObj = {
  modules: true, // ← これ
  sourceMap: isDevelopment ? true : false, 
  localIdentName: '[name]-[local]-[hash:base64:5]',
  importLoaders: 1 // https://www.npmjs.com/package/postcss-loader#integration-with-css-modules
}
```
---

## クラス名<br>↓<br> [ファイル名]-[クラス名]-[ハッシュ値]

---

- BEMのように人力で行っていたローカル化を、webpackが自動で行ってくれる。いわば、ローカルCSSのオートメーション化。

- クラス名の衝突を開発者が気にする必要がなくなった。
- ちなみに、セレクタにIDやHTMLのタグ名を使うことはできるけど、ローカル化はされない。

<sub>参考リンク：[[意訳]グローバルCSSの終焉](http://qiita.com/chuck0523/items/9a3cc6a2190dfb71a684)</sub>


--- 

ただし、css-loaderの仕組みは、\<style />タグの動的生成。
→レンダリング時にスタイリングのラグが発生しうる。

そこで、[ExtractTextPlugin](https://github.com/webpack/extract-text-webpack-plugin)を使う。具体的には、コンパイル時にローカル化済みのCSSファイルを生成する。

---

## ExtractTextPluginコンフィグ

```js
// webpack.config.js
module: {
  loaders: [
    {
      test: /.css$/,
      loader: ExtractTextPlugin.extract([`css?${cssLoaderQuery}`, 'postcss'])
    }
  ]
},
plugins: [
  new ExtractTextPlugin("[name].css") // output CSS
]
```

これで\<style/>、というか新しいCSSファイルが静的に生成される。これをHTMLから読み込む。

---

## これでローカルCSSを静的に読み込むことができる

--- 

## JS(X)との整合性

---

静的に生成されるCSSファイルでは、クラス名は既にローカル化されている

```css
.common-button-1DYla {
  // here comes style
}
```

なので、JSX側で

```jsx
<button className={button} />
```

としても、マッチせず、スタイルがあたらない。なので、JSにはローカル化されたクラス名を読み込む必要がある。

---


とは言えカンタン。CSS Modulesのもう１つの機能として、__ローカル化したクラス名のオブジェクト__を渡してくれる。

なので、まずはcssをimportして、そのプロパティであるクラス名をclassNameに割り当てる。

```jsx
import buttonStyles from '../stylesheets/Button.css'

<button className={buttonStyles.button} />
// buttonStylesオブジェクトにクラス名がぶらさがっている。
```

<sub>複数クラス名の同時指定については、もう１工夫が必要。
cf. [classnames-loader](https://github.com/itsmepetrov/classnames-loader)</sub>

--- 

## cssnext

---

![Screen Shot 2016-08-18 at 15.13.27.png](https://qiita-image-store.s3.amazonaws.com/0/77614/1c2e3132-5810-fb95-47b2-23deebb631cd.png)


---

## cssnextとは

未来のCSSを先取りしたもの。策定予定の仕様が盛り込まれている。

<img width="623" alt="Screen Shot 2016-08-18 at 15.15.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/77614/ad0ff4dc-cf8b-e873-572d-a089ad2844f7.png">

---

## CSSファイルでネストが使える（嬉しい）

---

## Sassとは若干シンタックスが異なる

```css
.container {
  & .button {
    color: #fff; 
  }
}
```

ネストするには、```＆``` が必要。

---

## 脱Sassしてみる

---

## 脱Sass

- Sassはオールインワン的。便利だけど、機能ありすぎ感。
- Ruby製のツールなので、フロント的になんだか落ち着かない感。
- node-sassとか、lib-sassとか、よく分からない…。

というわけで、脱Sassして、cssnextを使ってみることに。

---

## PostCSS

---

cssnextはそのままでは使えない。ブラウザが読める形式に変換する必要がある。

そのためにPostCSSを使う。
より具体的に言うと、PostCSSの[postcss-cssnext](https://github.com/MoOx/postcss-cssnext)を使う。

逆に言うと、それ以外のPostCSSプラグインはとりあえず使わない方向で。（オレオレCSS化するのを防ぐ）

<sub>参考リンク: [PostCSSとcssnextで最新CSS仕様を先取り！](https://html5experts.jp/t32k/17235/)</sub>

---

## cssnextとPostCSSはES201*とbabelの関係

---

<sub>ここまでのwebpackコンフィグを抜き出すとこんな感じ。</sub>

```js
module: {
  loaders: [
    {
      test: /.js?$/,
      loader: 'babel',
      exclude: /node_modules/
    },
    {
      test: /.css$/,
      loader: ExtractTextPlugin.extract([`css?${cssLoaderQuery}`, 'postcss'])
    }
  ]
},
postcss: () => {
  return [
    postcssCssnext()
  ]
},
plugins: [
  new ExtractTextPlugin("[name].css") 
]
```

<sub>※ cssローダーのクエリは外出ししている。 ※ ExtractTextPluginの引数の[name]はentry依存。</sub>

---

## reset.css 

いわゆるリセットCSS的なものも使っている。
ただし、これらはグローバルであってほしいものなので、ローカル化のプロセスをパスしてwebpackのバンドルに直に混ぜている。。

```js
entry: {
  application: [
    './app/frontend/stylesheets/reset.css', // ← リセットCSS
    './app/frontend/javascripts/application'
  ]
},
```

生成後のCSSファイルの先頭に定義されるように、./app/frontend/javascripts/applicationよりも前に指定している。

---

## おしまい

---

## (存外読まれているので追記)

---

話の方向性としては、以下のようになりました。

- Sassとのメリット・デメリットの比較をもっと詳しく行おう。
- そもそもネストって必要なのか？ネストが不要ならばcssnextは不要化する。
- デザインの流動性にコンポーネントの世界は耐えられるのか。

そんなわけで、そのうちまた続きを書くかもしれません。

---