cssnanoによる最適化まとめ

[cssnano](https://cssnano.co/)は、PostCSSの枠組みの中でCSSを最適化するツールですが、最適化項目も大量にあります。どのようなものがあるのかまとめてみました。

## 前置き～CSS用語の整理

説明に使うために、CSS用語を確認しておきます（[MDN](https://developer.mozilla.org/ja/docs/Web/CSS/Syntax)）。

```css
p {
  font-size: 12px;
  color: green;
}
```

* **規則セット**…セレクタと宣言ブロックの組。上の例では、全体が1つの規則セットとなっています。
* **セレクタ**…CSSをかける対象の指定。上の例では、`p`。
* **宣言ブロック**…セレクタに対して適用される、宣言の0個以上の組。上の例では`{ ... }`の箇所。
* **宣言**…プロパティと値の組。上の例では`font-size: 12px`と`color: green`[^semi]。
* **プロパティ**…スタイルを指定する特性。上の例では`font-size`と`color`。
* **値**…実際にスタイルとして指定する値。上の例では、`12px`と`green`。

## 最適化ルールの一覧

[原文](https://cssnano.co/docs/what-are-optimisations/)ではアルファベット順に並んでいますが、ここではわかりやすいように小さい方から大きな方へ順に並べていって、そして最後にデフォルトで適用されないルールについて触れます。

### 値レベルの最適化

* `calc`（[詳細](https://cssnano.co/docs/optimisations/calc/)）…CSSの`calc`で算出する値のうち、事前に計算できる部分は計算してしまう
* `colormin`（[詳細](https://cssnano.co/docs/optimisations/colormin/)）…色指定を、同じ色の中でできるだけ短い表記のものに置き換える（`white`→`#fff`など）。
* `convertValues`（[詳細](https://cssnano.co/docs/optimisations/convertvalues/)）…長さ・時間・角度の指定を、同じ量でできるだけ短い表記のものに置き換える。
* `minifyFontValues`（[詳細](https://cssnano.co/docs/optimisations/minifyfontvalues/)）…`font-family`をできるだけ短い表記になるようにしたり、`font-weight`を数字に置き換えたりする
* `minifyGradients`（[詳細](https://cssnano.co/docs/optimisations/minifygradients/)）…`linear-gradient`などの表記を短くなるように変換する
* `normalizeDisplayValues`（[詳細](https://cssnano.co/docs/optimisations/normalizedisplayvalues/)）…`display: 外側 内側`の2値指定されたもの[^display]を、1つの値に変換可能なら変換する
* `normalizePositions`（[詳細](https://cssnano.co/docs/optimisations/normalizepositions/)）…`background-position`などの値を短い形に変換する
* `normalizeRepeatStyle`（[詳細](https://cssnano.co/docs/optimisations/normalizerepeatstyle/)）…`background-repeat`などの値を短い形に変換する
* `normalizeString`（[詳細](https://cssnano.co/docs/optimisations/normalizestring/)）…文字列の引用符を統一し、行継続で入っている改行も削る
* `normalizeTimingFunctions`（[詳細](https://cssnano.co/docs/optimisations/normalizetimingfunctions/)）…`transition-timing-function`などの値を短い形に変換する
* `normalizeUnicode`（[詳細](https://cssnano.co/docs/optimisations/normalizeunicode/)）…`unicode-range`で、`?`で書けるものを`?`で書いて表記を短縮する
* `normalizeUrl`（[詳細](https://cssnano.co/docs/optimisations/normalizeurl/)）…`url()`の引用符を外す、解決できる相対パスを解決するなどして、表記を短縮する
* `orderedValues`（[詳細](https://cssnano.co/docs/optimisations/orderedvalues/)）…`border`や`transition`のように、バラバラな種類の値を複数指定できるもので、値の順番を統一する（結果、圧縮率が高まる）
* `reduceInitial`（[詳細](https://cssnano.co/docs/optimisations/reduceinitial/)）…`initial`の実際の値がもっと短い表記なら、それに置き換える
* `reduceTransforms`（[詳細](https://cssnano.co/docs/optimisations/reducetransforms/)）…同じ`transform`をより短い書き方に変更する
* `svgo`（[詳細](https://cssnano.co/docs/optimisations/svgo/)）…Data URIで埋め込まれたSVGを、svgoで最適化する

### 宣言レベルの最適化

* `discardDuplicates`（[詳細](https://cssnano.co/docs/optimisations/discardduplicates/)）…全く同じ宣言、規則セット、at-ruleを削除する
* `mergeLonghand`（[詳細](https://cssnano.co/docs/optimisations/mergelonghand/)）…四辺がバラバラに定義された`margin`、`padding`、`border`を1つのショートハンドにまとめる

### セレクタの最適化

* `minifySelectors`（[詳細](https://cssnano.co/docs/optimisations/minifyselectors/)）…セレクタの余計なスペース、引用符、`*`などを消して短縮する
* `uniqueSelectors`（[詳細](https://cssnano.co/docs/optimisations/uniqueselectors/)）…重複するセレクタを1つにする

### 規則セットレベルの最適化

* `discardDuplicates`（前述）
* `discardEmpty`（[詳細](https://cssnano.co/docs/optimisations/discardempty/)）…空の規則セット、at-rule、セレクタのない宣言ブロックを削除する
* `mergeRules`（[詳細](https://cssnano.co/docs/optimisations/mergerules/)）…隣接していて、「セレクタが同じ宣言ブロック」あるいは「全く同じ宣言のある宣言ブロック」をまとめる

### at-ruleの最適化

* `discardOverridden`（[詳細](https://cssnano.co/docs/optimisations/discardoverridden/)）…全く同じ名前で定義されて上書きされた`@keyframes`や`@counter-style`を削除する
* `minifyParams`（[詳細](https://cssnano.co/docs/optimisations/minifyparams/)）…at-ruleの引数から余計な空白を削る

### その他
* `discardComments`（[詳細](https://cssnano.co/docs/optimisations/discardcomments/)）…コメントを削除する
* `normalizeCharset`（[詳細](https://cssnano.co/docs/optimisations/normalizecharset/)）…`@charset`を（付けるとしたら）先頭の1つだけにする
* `normalizeWhitespace`（[詳細](https://cssnano.co/docs/optimisations/normalizewhitespace/)）…削れる空白・改行を削って、最後の宣言の後のセミコロンも落とす

### デフォルトでは無効なもの

これらは、「他のCSS/JSと連携する場合、意味を変えてしまう」などの要素があるものです。

* `autoprefixer`（[詳細](https://cssnano.co/docs/optimisations/autoprefixer/)）…不要なベンダプレフィックスを削る
* `discardUnused`（[詳細](https://cssnano.co/docs/optimisations/discardunused/)）…未使用のat-ruleを削除する
* `mergeIdents`（[詳細](https://cssnano.co/docs/optimisations/mergeidents/)）…同じ内容のat-ruleをまとめる
* `reduceIdents`（[詳細](https://cssnano.co/docs/optimisations/reduceidents/)）…at-ruleの名前を短縮する
* `zindex`（[詳細](https://cssnano.co/docs/optimisations/zindex/)）…`z-index`の値を整理する

## 使う上で心がけたいこと

まず、デフォルトで入っているものについては、正しく文法を解釈する環境にいる限りは安全です。流石に今どき使うことはまずないとは思いますが、文法違反、あるいは文法的には無意味だけど実際の挙動が違う、というような書き方を利用したCSSハックは正常に動作しなくなると思われます。

あと、重複削除などの機能があるので、cssnanoはプロセスの最後に近いところでかけるほうが効果的となります。Webpackであれば、`postcss-loader`として入れるのではなく、`optimize-css-assets-webpack-plugin`として入れたほうがいいでしょう。

## 脚注

[^semi]: セミコロンは宣言と宣言を区切るものであり、宣言の一部ではありません。
[^display]: 現時点ではFirefoxのみの対応となっており、実用する機会はまだまだ先かと思います。