Autoprefixer ― CSSのベンダー接頭辞をいろいろする

以前CSS-Tricksの記事で知ったAutoprefixerというの、ようやく試してみた。

CSS-Tricksの記事読んだほうがいいと思うけど、月一のブログ更新ノルマのためここでも書いとく。

かしこい「ポスト」プロセッサ

Autoprefixerはその名前からある程度想像できる通り、ベンダー接頭辞を自動的につけたりしてくれるユーティリティ。CSSファイルを作るのではなく、CSSファイルを処理するので、ポストプロセッサと言っている。

接頭辞まわりは、すでにCompassとかのライブラリでmixinなどが用意されてるので、べつに必要ないじゃんと思うかもしれない。ただ、プリプロセッサのmixinや野良mixinってちょっと雑で、吐き出されるCSSにあまり2013年感がない(個人の感想です)。

あと、とくに野良mixinに多いけど、ただ単に接頭辞つけただけで、仕様の構文が変わった場合に対処できてないものが多いかなと。無駄もしくは無意味なプロパティがCSSに書き出されてるのをちまちま見る。処理前のファイルはクールに書けても、出力されたCSSはクールじゃない。

Autoprefixerはcaniuse.comのデータをもとに、必要だろうとされるバージョンを判定して、そういう無駄な接頭辞を書き出さないようにしている。Compassも接頭辞の出力をカスタマイズできるけど、設定しないといけないのでちょっと煩わしい。自動でやってくれると楽だよね。

試してみた

ではどんなものかということで、試そう。コマンドラインはもとよりGruntやSublime Textなど、いろいろな環境で使える。今回はnpmからインストールして、コマンドラインで試してみた。

まずはborder-radiusbox-shadowの組み合わせ。こんなファイルを用意した。

.button {
    border-radius: 3px;
    box-shadow: 0 0 5px 1px #999;
}

雑な定義なのは気にしない。コマンドラインからAutoprefixerをかけた新しいファイルを出力。

% autoprefixer button.css -o button_a.css

autoprefixerって打つのだるい。あと-oってOperaの接頭辞つけるのかって気分になる。
どうでもいいね。結果はこうなった。

.button {
  border-radius: 3px;
  -webkit-box-shadow: 0 0 5px 1px #999;
  box-shadow: 0 0 5px 1px #999;
}

border-radiusは接頭辞なし、box-shadowはGingerbread用に-webkit-box-shadowが付け足されて出力されている。おお。

構文の変更もがんばってくれてる

接頭辞ついた版をつけてくれるのは分かった。では、構文の変更にはどれくらい対処してくれるのか。Autoprefixerのソースを見るとhacksというディレクトリがあって、構文の変更があるプロパティとか表記系を処理するファイルは、ここに個別に用意してあるようだ。

では、ぼくが愛を超えた憎しみみたいなガンダムにありそげな感情を持ってる(持ってない)linear-gradient() はどうなるか。こんなファイルを用意した。

.gradients {
    background-image: linear-gradient(#eee, #ccc 50%, #aaa);
}
.gradients-keyword {
    background-image: linear-gradient(to top left, #eee, #ccc 50%, #aaa);
}
.gradients-angle {
    background-image: linear-gradient(30deg, #eee, #ccc 50%, #aaa);
}

結果はこちら。

.gradients {
  background-image: -webkit-gradient(linear, top left, bottom left, from(#eee), color-stop(50%, #ccc), to(#aaa));
  background-image: -webkit-linear-gradient(#eee, #ccc 50%, #aaa);
  background-image: linear-gradient(#eee, #ccc 50%, #aaa);
}
.gradients-keyword {
  background-image: -webkit-gradient(linear, bottom right, top left, from(#eee), color-stop(50%, #ccc), to(#aaa));
  background-image: -webkit-linear-gradient(bottom right, #eee, #ccc 50%, #aaa);
  background-image: linear-gradient(to top left, #eee, #ccc 50%, #aaa);
}
.gradients-angle {
  background-image: -webkit-linear-gradient(60deg, #eee, #ccc 50%, #aaa);
  background-image: linear-gradient(30deg, #eee, #ccc 50%, #aaa);
}

おお、ちゃんと-webkit-linear-gradient()iOS 6まで)と-webkit-gradient()(Gingerbread用)に対応して、あとは接頭辞なしの物を使うようになっている。キーワードの変換も、角度の変換も、どちらもちゃんとできてるし、角度が使えない -webkit-gradient() については、ちゃんと無視されている。すばらしい。

ただ、IE9用にSVGのフォールバック画像をつくったり、IE8以下用にフィルタをつけてくれたりはしないようだ。hacks/gradient.coffeeなるファイルがあったので見てみたけど、そういう処理はなさげ。ふーむ。とはいえどちらも完全なハックだし、IE9は順調にIE10に置き換わってるようだし、IE8以下は「もうプレーンでよくない?」って言え…ないかな……

要らないものを消してもくれる

さて、ある程度試したところで、つけたすだけじゃなくて、要らないものを省いてくれることに気づいた。なんと。ではさっそく。

.so-200x {
    -webkit-border-radius: 5px;
    -moz-border-radius: 5px;
    border-radius: 5px;
}

結果。

.so-200x {
  border-radius: 5px;
}

おおお。すっきりした。じゃあCompassから出力されたものも、それなりに綺麗にしてくれるのかも。

「ポスト」のよしあし

結構よさげ。仕様のとおりに書いとけばあとは勝手にやってくれるので、簡単なmixinさえ書く必要がない。仕様に変更があっても、あたまの良い人達がきっとすぐ対応してくれるだろうし、すこし複雑なmixinとかも書かなくてもいい。いったいぼくのあの日々はなんだったんだ。

あとは、ポストプロセッサなので、特定のプリプロセッサに依存しないのもよい。プリプロセッサを乗り換えるってことはそんなないだろうけれど、それでもポータビリティは上がるかなと。

とはいえ、ポストプロセッサなところが弱点でもあるかと。主に開発時に面倒に感じることが多そう。

たとえば、プリプロセッサとの併用。Chrome+Sassな環境なら、Source Mapsやデバッグ用の出力モードで、CSSパネルからSassを参照できる。ただ、ポストプロセッサが入ってしまうとマッピングがずれてしまう。Autoprefixer側でプリプロセッサが生成したSource Mapsを解釈して、新しいマッピングファイルを作ってくれればよいのだけど、まだ対応されてない

あとは、どうやら接頭辞つきのみのコードに、接頭辞なしのものを含めてくれるわけではないみたい。となると、たとえばChromeでアニメーションのコードを書いているときは、@-webkit-keyframesしか書かないことがほとんどだと思うけど、そこから@keyframesは生成されない。でもCSS Animationsの接頭辞が省かれるのはだいぶ先だろうし、でも開発中は接頭辞必要だろうし…というジレンマに陥る。

となると、開発中は接頭辞つき、展開時は接頭辞なしのものを吐き出すようにプリプロセッサやツール周りで対応…とかややこしいことになる。接頭辞はだいぶ減ってきたとはいえ、まだまだお世話?になると思うので、ここはなんとかして欲しいなあ。

もいっこ。CSSをパースして処理する関係上、インデントやらが独自のものにされてしまう。これは展開時にCSSOなりして再処理が走るなら、そんな気にならないだろうけど。