カオスになりがちなCSSと向き合ってみた

機能追加や既存機能の修正、デザイン変更など、プロダクト開発やサイト制作をしていると、完全分業制ではない限り、デザイナーやコーダーだけでなくアプリケーションエンジニアもCSSに触れる機会があります。

実際に私が関わるプロジェクトでは、CSSを聖域扱いにしていないので、開発チームの誰もが必要に応じて触っています。
ですが、CSSはカオス化の危険性を多分に含んだ、ゆるーい言語なので取り扱いには注意が必要なのです。

## カオスで汚いCSS!?

以下のどれか一つでも心当たりあれば、高確率であなたのCSSはカオス化しています...

1. スタイルの上書き数が多い
2. プロパティ順がぐちゃぐちゃ
3. どれを変更すれば良いかがわかりにくい

### 1. スタイルの上書き数が多い

![conflict-style.jpg](https://qiita-image-store.s3.amazonaws.com/0/53286/ed6ed18b-28fb-033a-4dbc-4d64f8475e45.jpeg)

Chromeの開発ツールで見ると、プロパティに打ち消し線が引かれて、別の箇所で上書きされていることがわかります。
`normalize.css` や `reset.css` などの読み込みやベンダープリフィックスによる上書きは仕方がないですが、それ以外は回避できるはずで、もっと言えば、必要最低限以上は上書きされないようなCSS設計が好ましいです。

上書きの数だけ、不要なスタイルを書いていることになるので、見つけ次第撲滅していくのが鉄則で、先ほどの例だと以下のようにするとスリムになります。

```css
.btn {
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
  background: none;
  border: none;
  cursor: pointer;
  outline: 0;
  -webkit-transition: all 0.2s cubic-bezier(0.4, 0, 0.4, 1);
  transition: all 0.2s cubic-bezier(0.4, 0, 0.4, 1);
}

.btn.btn-primary {
  background-color: #2188ff;
  border-radius: 2px;
  -webkit-box-shadow: 0 0 2px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.2);
          box-shadow: 0 0 2px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.2);
  color: #fff;
  display: block;
  font-size: 14px;
  line-height: 1;
  padding: 8px 12px;
}
```

`.btn` にはボタンのベーススタイルだけを定義し、 `.btn.btn-primary` のようにマルチクラスとして使用するようにし、 `.btn-primary` が他のスタイルに干渉しないようにしました。

![fixed-style.jpg](https://qiita-image-store.s3.amazonaws.com/0/53286/d42d59e0-7ac1-b1fa-7982-78b5a3dea9ee.jpeg)

これでベンダープリフィックス以外の上書きがなくなりましたね。
さらに、上の画像のErrorボタンのようなスタイルを追加したいときは以下のようにします。

```css
.btn.btn-error {
  background-color: #e53935;
  border-radius: 2px;
  -webkit-box-shadow: 0 0 2px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.2);
          box-shadow: 0 0 2px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.2);
  color: #fff;
  display: block;
  font-size: 14px;
  line-height: 1;
  padding: 8px 12px;
}
```

実際はもっと複雑にスタイルが重複しているかもしれませんが、 `.btn` あるいは `.button` や `.modal`, `.tab` など、複数の箇所で使われる可能性が高いものは、ベースとなるCSSクラスに共通部分のスタイルだけを定義し、役割や見た目は別のCSSクラスで追加していくようにすると上書きされるリスクも減り、見やすいコードになります。
もし、上書きの影響でスタイルが思うように適用されない場合は、

```css
.btn.btn-error {
  background-color: #e53935 important!;
}
```

のように `important!` を使えば実現可能ですが、運用面で考えると、`important!` はスタイル適用の優先度が最も高いため、

- 他の箇所で上書きされたくない
- 一時的、もしくは強制的にスタイルを適用させる必要がある

といったケースにのみ使うべきです。
`important!` をつけて定義されたスタイルを上書きできるのは、それ以降に定義される `important!` だけなので、ルールなく`important!`を使うことはカオスなコードを書いている兆候で、**上書きの影響でスタイルが思うように適用されなかったので `important!` しました** の回数が多い人は高確率でカオスなコードを書いています。

### 2. プロパティ順がぐちゃぐちゃ

```css
.btn {
  font-size: 14px;
  font-weight: 700;
  line-height: 20px;
  display: inline-block;
  padding: 12px 24px;
  cursor: pointer;
  vertical-align: top;
  color: #fff;
  border: none;
  border-radius: 3px;
  background-color: #ececec;
}
```

何も考えずにプロパティを並べましたが、他のプロパティを追加する時はどこに書けばよいでしょうか。
規則性がわからないので、「とりあえず最下部に追加しとくか」となりそうです。

#### 皆がわかりやすいルールで幸せに

モデルや役割順、機能順、`display: none` などのレンダリングに影響与えるもの順など、CSSプロパティの並べ方はいくつかあり、導入するフレームワークやCSS設計によっても変わる場合があります。  
ちなみに、私はアルファベット順(AtoZ)が好きで、それは誰もが理解しやすいからです。(決して並び順を考えたり指定するのが面倒だからではありません...)

```css
.btn {
  background-color: #ececec;
  border: none;
  border-radius: 3px;
  color: #fff;
  cursor: pointer;
  display: inline-block;
  font-size: 14px;
  font-weight: 700;
  line-height: 20px;
  padding: 12px 24px;
  vertical-align: top;
}
```

プロパティを AtoZ で並べてみました。先ほどよりは、追加や修正したいプロパティを発見しやすくなったのではないでしょうか。 ちなみに、この並び順にすると、クラスセレクタ内で同じプロパティを書くこともなくなります。

どの並び順を採用するかについては、プロジェクトや案件、それに関わるチームの構成によって決めるとして、とにかく、プロパティの並び順に一貫性がない人はカオスなコードを書いています。


### 3. どれを変更すれば良いかがわかりにくい

Sass(SCSS)のようなプリプロセッサが使える環境である場合、どういう方針でファイルを管理するのかという話です。

私事ですが、最近、開発チームでこれについて議論を重ね、**URLを見ればどのCSSが読み込まれているかわかる**という方針に変更し、地道な作業を経て、現在に至ります。 

具体的には
- CSS(SCSS)のフォルダ構成とファイル名をURLに合わせ `views` フォルダで管理する
- 再利用可能なコンポーネントはそれぞれのCSSファイルを作成し、`components` フォルダで管理する
- 変数や定数、ヘルパーなどのスタイルは `lib` フォルダで管理する

```
├── styles
│   ├── components
│   ├── lib
│   ├── views
│   │   ├── aaa
│   │   └── bbb
│   └── index.scss
```

例えば、`http://hoge.com/users/new` というURLに対応するCSSファイルの置き場所は `プロジェクトルート/styles/views/users/new/index.scss` となります。ページ数によってviews配下のボリュームが大きくなりますが、対応するCSSファイルがどこにあるのかがすぐにわかるので、**説明が楽！**になりました。 
この変更は開発チームで好評だったため、JSも同じフォルダ構成に変更しました。

どこでスタイルを設定しているかわからず、スタイル迷子になっている人はカオスなコードを書いています。

## おまけ: CSSの整形は自動化できると幸せ

CSSプロパティの並び順などのルールを設定した場合、人間の手で毎回整形するのはとても辛くミスも発生します。
[CSScomb](http://csscomb.com/)という、自動整形してくれるライブラリがあり、導入も楽なのでおすすめです。

開発者毎に利用しているエディタが異なるので、設定方法は以下を参考にもしくは、ググってください。

- https://github.com/csscomb/jetbrains-csscomb
- https://github.com/csscomb/sublime-csscomb
- https://github.com/csscomb/vim-csscomb
- https://atom.io/packages/atom-csscomb

### 設定方法

1. プロジェクトルートに `.csscomb.json` を作成
2. http://csscomb.com/config で設定を作成し `.csscomb.json` に保存

準備はこれだけで、あとはエディタなどで `Run CSSComb` すれば自動整形してくれます。

![csscomb.gif](https://qiita-image-store.s3.amazonaws.com/0/53286/567eba98-87b2-1c19-113c-f6e9fe1a7f91.gif)

### 設定例

実際に私たちの開発チームで使用している設定を晒します。

```json:.csscomb.json
{
  "remove-empty-rulesets": true,
  "always-semicolon": true,
  "color-case": "lower",
  "block-indent": "  ",
  "color-shorthand": true,
  "element-case": "lower",
  "eof-newline": true,
  "quotes": "double",
  "sort-order-fallback": "abc",
  "sort-order": [
    [
      "$variable"
    ],
    [
      "$import",
      "$extend",
      "$include"
    ],
    [
      "..."
    ],
    [
      "$selector"
    ],
    [
      "$include media"
    ]
  ],
  "space-before-colon": "",
  "space-after-colon": " ",
  "space-before-combinator": " ",
  "space-after-combinator": " ",
  "space-between-declarations": "\n",
  "space-before-opening-brace": " ",
  "space-after-opening-brace": "\n",
  "space-after-selector-delimiter": " ",
  "space-before-selector-delimiter": "",
  "space-before-closing-brace": "\n",
  "strip-spaces": true,
  "tab-size": true,
  "unitless-zero": true
}
```

並び順はAtoZとし、セレクタ内は変数、`@import`、`@extends`、`@include`、メディアクエリとなるように調整しており、実際には以下のように整形されます。

```scss
.modal {
  $modal-size: 300px;

  @import "../lib/variables";
  @import "modal";
  @include size($modal-size);

  align-items: flex-start;
  display: flex;
  flex-wrap: wrap;
  left: 50%;
  margin: 30px;
  padding: 20px;
  position: absolute;
  top: 50%;
}
```

細かい並び順にはさまざまな意見があるかもしれませんが、私たちのチームでは変数を `variables.scss` で管理し、それぞれに `!default` をつけて定義しています。

```scss
$sample: $value !default;
```

こうすることで、変数格納時に**既に定義済みではない限りは**という制約がつくので、もし、上書きしたい変数があれば `@import "../lib/variables";` の前に定義すればいいので、CSSCombのsort-orderでは、`$variable` が一番上になるというルールにしています。

---

カオスなCSSに少しでも秩序をという想いでここまで書いてきましたが、もう少し厳しくコードを管理したい、ルールから外れた場合はエラーを出したい時にはCSSの構文チェックツールの[stylelint](https://github.com/stylelint/stylelint)の導入を検討しても良いかもしれません。
ちなみに、CSSCombのように設定ファイルを作ってくれる[Stylelint Config Generator](https://maximgatilin.github.io/stylelint-config/)もあります。

過去に自分が書いたCSSを見返したり、変更する機会があると、「結構やばいコード書いているな」と思うときがあり、その多くは先述したカオス化のどれかに当てはまっていて、発見次第しれっと直してましたが、最近ではルールを作ったことでその頻度も減ってきて、ちょっとずつカオスなCSSから卒業できている気がします。

デザイナでもエンジニアでもCSSに触れる人であれば、最小限の説明で理解できる、そういう状態に早くならないかな...