PostCSS まとめ

僕はPostCSSは開発当初から追いかけていて、GitHubでPostCSSチームのメンバーのひとりだったり、実際にPostCSSを使ってプラグインやツールをいくつか作ったり、ブログや雑誌で記事を書いたり、勉強会等で話したりしています。

本記事では、自分が知っているPostCSSについての事柄を共有します。

## PostCSSとは何か

PostCSSとは、ロシア人の [Andrey Sitnik](https://github.com/ai) という人が開発している、Node.js製の「CSSツールを作るためのフレームワーク」です。PostCSS製のツールとして、ベンダープリフィックスを自動で付与する「Autoprefixer」、未来のCSSの構文の一部を今のブラウザで解釈できるようにする「cssnext」、カスタマイズ性に富んでいるCSSリンターである「stylelint」などが有名です。

PostCSSは2018年2月、かなり広く使われており、GitHubではスター数が約18000スターとなっています。PostCSSを使用している企業やサービスとして、Facebook, GitHub, Google等、日本では [Qiitaで導入しています](http://qiita.com/morishitter/items/c8773b6ca69b3576626a)。

### PostCSSの仕組み

実装としてはPostCSS自体はただのCSSのパーサーで、それほど大きいコードベースではありません。

PostCSSは入力としてCSSを受け取り、それをパースし独自のASTを生成します。ASTの操作はプラグインが担当しており、PostCSS自体はCSSに対して一切変更を加えません。開発者は、PostCSSのプラグインを作ることで、独自の処理をCSSにおこなうことができます。そして、変更されたASTを文字列に戻すことで、新しいCSSを出力します。

![PostCSSの処理の流れ](https://qiita-image-store.s3.amazonaws.com/0/6598/ecb8a9db-bd0f-5451-2592-d21f617815d2.png)

また、PostCSSは[ASTを操作するためのAPIを提供](http://api.postcss.org/)しており、これらを利用することで簡単に、効率良く変換処理を書くことができます。


## PostCSS製ツール

Webアプリケーションを作る業務では、PostCSSは既存のプラグインを組み合わせて使うことが多いと思います。CSSの開発フローの中から、プリプロセス、最適化、ドキュメント作成、コードリントなどのフェーズで使うPostCSSプラグインをいくつか紹介します。

PostCSSを使うメリットのひとつは、一連のCSSのビルドフローを全てPostCSSのエコシステムで解決できるところです。

### プリプロセッサーとしてのPostCSSプラグイン

まずは、プリプロセッサーとして使えるプラグインについて。PostCSSには[Custom Syntaxes](https://github.com/postcss/postcss/blob/master/docs/syntax.md)という機能があり、PostCSSの"Parser"と"Stringifier"の部分もカスタマイズすることができます。既存のものとしては以下のような実装があります。

- [postcss-scss](https://github.com/postcss/postcss-scss): SassのSCSS記法のパーサー
- [sugarss](https://github.com/postcss/sugarss): インデントベースの独自記法
- [postcss-js](https://github.com/postcss/postcss-js): JSのオブジェクトとして書かれたCSSのパーサー
- [midas](https://github.com/ben-eb/midas): シンタックスハイライター (CSSコードではなくHTMLを出力)

また、独自の`@`ルールやプロパティをフックして言語拡張をしているプラグインもあります。ではその中からいくつか紹介します。

#### postcss-custom-properties

[postcss-custom-properties](https://github.com/postcss/postcss-custom-properties)は、[Custom Properties](https://drafts.csswg.org/css-variables/)のシンタックスでグローバル変数定義できるようにするプラグインです。PostCSSがCSSをCSSに変換する都合上、Custom Propertiesの **ポリフィルではない** ので注意が必要です。下記の例のように `:root` セレクタのルール内でのみ定義可能です。

```css
:root {
  --mainColor: #59bb0c;
}

.Logo {
  fill: var(--mainColor);
}
```

#### postcss-custom-media

[postcss-custom-media](https://github.com/postcss/postcss-custom-media)は、メディアクエリーのパラメーターに名前を付けられるようにするためのPostCSSプラグインです。Custom Media Queriesは[Media Queries Level 5で載る（かもしれない）](https://github.com/w3c/csswg-drafts/blob/3abb7e30e5a7ec5f7504f5bc6470cf3c9a123b37/mediaqueries/deferred-for-level-5.txt#L100-L238)仕様です。

```css
@custom-media --mobile (max-width: 415px);

@media (--mobile) {
  .selector {
      property: value;
  }
}
```

#### postcss-apply

CSSには複数のプロパティ宣言を組み合わせることでデザイン的な意味を持つものがあり、それに名前を付けて管理するための仕様を、[Custom Sets of Properties](http://tabatkins.github.io/specs/css-apply-rule/)といいます。この仕様はTab Atkins Jr.(CSSWGのメンバーであり、Googleのソフトウェアエンジニア。FlexboxやCustom Properties等のエディタ)という人が独自に提案しているものです。~~Chrome Canaryではフラグ付きで実装されています。~~

**2018/02/27 追記:** Custom Sets of Properties (`@apply`) は[仕様から落ちました](https://www.xanthir.com/b4o00)。

```css
:root {
  --truncate: {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  };
}

.text {
    @apply --truncate;
}
```

#### postcss-cssnext

上記3つのような、将来的にCSSの仕様として入る可能性のあるプラグインをまとめたものが [postcss-cssnext](https://github.com/MoOx/postcss-cssnext)(cssnext)です。

未来のCSSのシンタックスを書き心地を確認するのには良いものだと思います。

#### プリプロセッサーとしてPostCSSを使うメリット

今はSassやStylusを使っているが、「`@import`によるファイルの依存管理と変数定義しか使っていない」、「`@mixin`や`@extend`は管理が難しいから使っていない」という人もいるのではないでしょうか。PostCSSを使うと、必要な機能だけを選択して使用することができます。その分パフォーマンスも良く、ビルドを早くすることができるでしょう。

また、PostCSSのひとつひとつのプラグインは非常に小さく、コードを読んで自分で修正、コントリビュートすることも難しくありません。自らPostCSSのプラグインを書くことによって、独自の変換処理の構文を作ることもできます。

PostCSSをプリプロセッサーとして使うメリットは以下の2点です。

- パフォーマンス
- カスタマイズ性

### 最適化のためのPostCSSプラグイン

次はブラウザが解釈可能なCSSコードを受け取り、最適化をおこなうためのプラグインを紹介します。これらのプラグインはPostCSS製のプリプロセッサーだけでなく、SassやLess、Stylusとも併用可能（プリプロセッサーの出力を入力として受け取るから）です。

#### Autoprefixer

[Autoprefixer](https://github.com/postcss/autoprefixer)はPostCSSのプラグインとして最も有名なものだと思います。[Can I Use](http://caniuse.com)のデータを利用し、サポートブラウザを指定することで、必要なベンダープリフィックスを自動で付与するツールです。

input:

```css
.example {
  display: flex;
  user-select: none;
  -webkit-border-radius: 3px;
  border-radius: 3px;
}
```

output:

```
.example {
  display: -ms-flexbox;
  display: flex;
  -webkit-user-select: none;
     -moz-user-select: none;
      -ms-user-select: none;
          user-select: none;
  border-radius: 3px;
}
```

#### cssnano

[cssnano](https://github.com/ben-eb/cssnano)はCSSのminifyツールです。PostCSS製のminifyツールとしては他に、[CSSWring](https://github.com/hail2u/node-csswring)があります。

CSSのminifyツールにも性能に差はありますが、微々たるものなので好きなもの、自分にとって信頼できるものを使うと良いと思います。

#### postcss-sorting

[postcss-sorting](https://github.com/hudochenkov/postcss-sorting)はプロパティ宣言の順序をソートするためのプラグインです。CSSのプロパティ宣言の順序を揃えるのは、単にフォーマットのためだけではなく、[gzip圧縮時に圧縮率を高める可能性を高める](http://peteschuster.com/2014/12/reduce-file-size-css-sorting/) ことができます。

### コードリント

CSSにおいても、JavaScriptのような一般的なプログラミング言語と同様に、コードをリントすることで、チーム開発において記述を統一することができたり、実行時のエラーを事前に防ぐことができます。今日は、PostCSS製のモダンなCSSリンターである、[stylelint](https://github.com/stylelint/stylelint) について説明します。


#### stylelintのルール
stylelintで設定できる[ルールの一覧](http://stylelint.io/user-guide/rules/)を見てみましょう。


| ルール名          | 説明  |
|-------------| -----|
| `at-rule-name-case` | `@`ルール名が大文字か小文字か |
| `color-hex-length` | HEX指定のカラーコードを短縮するかどうか |
| `color-no-invalid-hex` | invalidなHEXカラーコードを許可するかどうか |
| `indentation` | インデントサイズ |
| `selector-max-specificity` | セレクタの最大詳細度 |

など、これらのルールを細かく指定することで、より自分好みのコーディング規約に違反しているかどうかを機械的に検知することができます。コードレビューの負担を減らすためにも、リンターの導入は検討されるべきです。

現在の最新バージョンである `v9.1.1` には、180個のルールがあります。CSS、またはSassなどのプリプロセッサーのためのリンターはいくつかありますが、その中でもstylelintのルールセット数が圧倒的に多く、JavaScriptのコードリンターであるESLintのように、カスタマイズ性に富んでいることがわかります。

CSSリンターのルール数:

|stylelint|CSS Lint|scss-lint|CSSComb|
|----:|---:|---:|---:|
|180|38|62|26|

また、stylelintはPostCSS製（パーサーとしてPostCSSを使っている）ため、CSSだけでなく、SassやLESS等のPostCSSがパース可能なシンタックス全てに対してリントすることが可能なのも特徴です。

#### 設定ファイルの書き方

stylelintの設定のオブジェクトは [cosmiconfig](https://github.com/davidtheclark/cosmiconfig) というパッケージを使って読み込まれます。stylelintの場合、以下のファイル名、形式で設定ファイルを作成できます。僕は、JSON形式で `.stylelintrc` という名前で作ることが多いです。

- `package.json` 内の `stylelint` プロパティに記述する
- `.stylelintrc` ファイル（JSON or YAML）
- JSオブジェクトとして `module.exports` された `stylelint.config.js` ファイル

`.stylelintrc` 例: 

```json
{
  "rules": {
    "color-hex-length": "short",
    "color-no-invalid-hex": true,
    "custom-property-no-outside-root": true,
    "indentation": 2,
    "length-zero-no-unit": true,
    "media-feature-name-no-vendor-prefix": true,
    "number-leading-zero": "never",
    "selector-root-no-composition": true,
    "string-quotes": "single"
  }
}
```




また、npmで、`stylelint-config` のキーワードを付けて登録されているパッケージは、stylelintの設定集です。その中でも、以下のパッケージが有名です。

- [stylelint-config-standard](https://github.com/stylelint/stylelint-config-standard): 最も一般的なCSSのコーディング規約集
- [stylelint-config-suitcss](https://github.com/suitcss/stylelint-config-suitcss): [SUIT CSSのコーディング規約](https://github.com/suitcss/suit/blob/master/doc/STYLE.md)
- [stylelint-config-primer](https://github.com/primer/stylelint-config-primer): GitHubのCSSのコーディング規約

stylelintのルールは今180個あり、それらを1つずつ調べて自分で1から設定ファイルを作るのは骨が折れます。
そこで、 `stylelint-config-*` パッケージを継承することで、公開されている設定ファイル集をベースに自分のプロジェクトでの調整を加えることができます。


```json
{
  "extends": "stylelint-config-primer",
  "rules": {
    "indentation": 2,
    "string-quotes": "double"
  }
}
```


また、stylelintのルールは、全てPostCSSのプラグインとして実装されています。そこで開発者は、独自のstylelintのルールを作って、それをstylelintのプラグインとして公開することができます。

例として、SassのSCSS記法のための、[stylelint-scss](https://github.com/kristerkari/stylelint-scss) というプラグインがあります。

```json
{
  "plugins": [
    "stylelint-scss"
  ],
  "rules": {
    "scss/dollar-variable-pattern": "^foo",
    "scss/selector-no-redundant-nesting-selector": true
  }
}
```

#### stylefmtによる自動整形

約180個あるstylelintのルールですが、`"indentation"` や `"string-quotes"`, `"color-hex-length"` など、大半はコードフォーマットに関するルールです。これらのフォーマットに関するルールは、ESLintの `--fix` オプションのように自動修正することが可能です。

PostCSSのエコシステムでは、[stylefmt](https://github.com/morishitter/stylefmt) というツールを使うことで、stylelintの設定ファイルから、フォーマットに関するルールに従いコードを自動整形することができます。ちなみに、[前はCSSfmtという名前でした](http://qiita.com/morishitter/items/7e58b0cc32797bf59538)。

stylefmtには[デフォルトのフォーマットルール](https://github.com/morishitter/stylefmt#default-formatting-rules-without-stylelint-config-file)が存在し、僕はstylelintの設定ファイルにはフォーマットに関するルールは一切書かず、stylefmtのデフォルトのルールに従っています。

### その他PostCSS製ツール

#### PreCSS

SCSS-likeなシンタックスを持つプリプロセッサーです。約1年間更新されておらず、ベンチマークもnode-sassと変わらないので、node-sass、もしくはdart-sassを使うことをオススメします :)

#### doiuse

[doiuse](https://github.com/anandthakker/doiuse)は、Can I Useのデータと入力するサポートブラウザを元に、サポート対象外のプロパティや`@`ルールを使っていないかをリントするツールです。Autoprefixerと同じ記法でサポートブラウザを指定することができます。

#### CSS Modules

[CSS Modules](https://github.com/css-modules/css-modules)という、CSSのルールセットの影響範囲がグローバルスコープである問題を解決するためのアプローチがあります。CSS Modulesを使って定義されたルールセットはデフォルトでローカルスコープ（ファイルスコープ）を持ち、`composes`というキーワードにより異なるスコープ間でのスタイルの共有も可能です。

ここではCSS Modulesの使い方や詳しい仕組みについては説明しませんが、セレクタ名にハッシュ値を付ける処理や、`composes`キーワードの実装にPostCSSが使われています。

参考: [CSS Modules 所感](http://morishitter.hatenablog.com/entry/2015/09/28/103334)


## 小ネタ

最後に、PostCSSについての小ネタを紹介します。

### 歴史

PostCSSのようなプラグインアーキテクチャーなCSSパーサーは、PostCSSがはじまりではありません。PostCSSの元となったのは、TJやNicolas Gallagherらが始めた、[rework](https://github.com/reworkcss/rework) というプロジェクトです。

PostCSSの作者のAndreyは、reworkを使ってrework-vendorsという、ベンダープリフィックスを自動で付与するためのプラグインを書きました。これが[後のAutoprefixerとなります](https://github.com/postcss/autoprefixer/commit/419a77d4d871a1d7be34ff7129e3cbf7fb755b0c)。しかし、当時reworkにはブラウザハックをパースできないバグがあったり、TJに「AutoprefixerはAPIがイケてない」、「CoffeeScriptだからダサい」等とDisられたことで、Andreyはreworkのようなツールを自分で作り、PostCSSが誕生しました。

今では、PostCSSはreworkよりもパーサーとしても優秀で、実行速度も速く、AST操作のための便利なAPIも提供されているので、多くのツールがPostCSSから作られるようになりました。

### ポストプロセッサー？

PostCSSはreworkとの差別化のために、PostCSSで作られたAutoprefixer等を「ポストプロセッサー」と命名し、PostCSSはポストプロセッサーを作るためのツールとしていました。しかし、cssnextやPreCSSといった言語拡張（プリプロセス）のためのツールも作られるようになり、[PostCSSチームは「ポストプロセッサー」という単語を使わない](https://twitter.com/PostCSS/status/626046993006239744?ref_src=twsrc%5Etfw)ことになりました。

参考: [CSSのプリプロセスとポストプロセス、そしてReworkとPostCSS](http://morishitter.hatenablog.com/entry/2014/12/07/000205)

---

## CHANGELOG

### 2018/02/27

- 紹介しているライブラリを最新のバージョンに対応