どうも、初めて書きますnju33(じゅん)という者です。
よろしくお願いします。
書く順番が回ってきて何を書こうか迷ったのですが、今回はPostCSSについて書こうと思います。
PostCSSとは?
Autoprefixerで有名なAndrey SitnikさんらがNodeJSで開発している、
CSSパーサとそれを取り扱うのに便利なAPIを提供しているツールです。
そのAPIを使って、変換する為のプラグインが沢山が各々によってたくさん作られています。
プラグインは1つで1つの事を行うことをポリシーに作られているので、それらを組み合わせることで、自分だけのCSSビルドツールを作ることができます。
最近では「その場で実行」系で人気サービス、 Codepen(*)や jsFiddle(*) なども続々と対応してきていたり、 「Twitter Bootstarp5にはPostCSSが使われるかも」と開発者の1人 Mark Otto(@mdo) さんが言っていたりと、今後に注目したいツールです。
Oh, btw—Bootstrap 4 will be in SCSS. And if you care, v5 will likely be in PostCSS because holy crap that sounds cool.
— Mark Otto (@mdo) 2015, 4月 23
PostCSSの使い方
まずは実際に変換してみます。
とりあえず、下の2つのプラグインを選びました。
プラグイン名 | 説明 |
---|---|
autoprefixer | ベンダープレフィックスを付ける |
postcss-namespace | セレクターにプレフィックスを付ける |
ではまずは、プラグインとPostCSSをインストールします。
# 作業ディレクトリとして postcss/ を作って移動 mkdir postcss && cd $_ npm install postcss autoprefixer postcss-namespace
次に、変換用のcssファイルを作ります。
@prefix block; .box { display: flex; }
最後に、PostCSSを使って変換するjsファイルを作ります。
postcss
の第一引数に、インストールしたプラグインを適応順に、配列で記述します。
// 必要なモジュール読み込み const fs = require('fs'), postcss = require('postcss'), autoprefixer = require('autoprefixer'), namespace = require('postcss-namespace'); // autoprefixerで変換されたCSSを、 // 次はpostcss-namespaceで変換 const processor = postcss([ autoprefixer({browsers: ['last 2 versions']}), namespace({token: '__'}) ]); // 変換するファイル const css = fs.readFileSync('style.css', 'utf-8'); processor.process(css) .then((result) => { console.log(result.css); });
ちなみに、ディレクトリ構造はこうなっています。
. ├── node_modules // 中身は略 ├── script.js └── style.css
あとは、変換するだけです。
node script.js
こんな感じに変換されました!
.block__box { display: -webkit-flex; display: -ms-flexbox; display: flex; }
プラグインの中には、いくつかのプラグインを集めて1つのプラグインとして提供している、
パックプラグインというものもあります。
例えば下の2つはそれです。
プラグイン名 | 説明 |
---|---|
cssnext | CSS4シンタックスに近い方法で書けるようにする |
precss | Sassのような感じで書けるようにする |
詳しくはそれぞれのリンク先を読んでみてください。 最初はこれらから使っていき、慣れてきたら1つ1つプラグインを選んでいくなどすると良いと思います。
僕のCSSビルドツール
最後に自分の環境を晒して終わりたいと思います。
タスクランナー
流石に変更の度にコマンドを実行するのは面倒くさいのでgulpを使って、 cssファイルが変更する度にビルドされるようにします。
以下のプラグインを使います。詳細は各プラグインのリンク先で。
プラグイン名 | 説明 |
---|---|
stylelint | cssの構文チェック |
postcss-reporter | stylelintログを見やすくして表示 |
postcss-cson-cssvars | CSS内で使える変数をCSONファイルで設定 |
postcss-color-function | カラー調整関数を追加 |
postcss-assets | より良い画像アセット読み込み関数を追加 |
postcss-property-lookup | 同じルール内のプロパティ値に、@xxx のような形で利用できる |
postcss-animation | animate.cssで定義されてる名前をanimation に指定するだけで使える |
postcss-easings | transition-timing-function にeasings.netにあるものを追加 |
lost | グリッドシステムなプロパティを追加 |
postcss-selector-not | CSS4シンタックスの:not が使える |
postcss-preref | 前回のセレクタを& で使える |
postcss-namespace | セレクターにプレフィックスを付ける |
doiuse | caniuseを元に、サポート対象ブラウザが対応していないプロパティを使っていると警告を出してくれる |
autoprefixer | ベンダープレフィックスを自動で付ける |
css-mqpacker | @media の最適化 |
cssnano | cssの最適化 |
const fs = require('fs'), gulp = require('gulp'), watch = require('gulp-watch'), sourcemaps = require('gulp-sourcemaps'), postcss = require('gulp-postcss'); const processors = [ require('stylelint'), require('postcss-reporter')({clearMessages: true}), require('postcss-cson-cssvars'), require('postcss-color-function'), require('postcss-assets'), require('postcss-property-lookup'), require('postcss-animation'), require('postcss-easings'), require('lost'), require('postcss-selector-not'), require('postcss-preref'), require('postcss-namespace')({token: '__'}), require('doiuse')({browsers: ['last 2 versions']}), require('autoprefixer')({browser: ['last 2 versions']}), require('css-mqpacker'), // require('cssnano') 見づらくなるので今回はコメントアウト ] gulp.task('postcss', () => { gulp.src('./style.css') .pipe(watch('./style.css')) .pipe(sourcemaps.init()) .pipe(postcss(processors)) .pipe(sourcemaps.write('.')) .pipe(gulp.dest('dist/')); });
たくさん読み込んでいますが、順番はかなり重要です。
順番によって後のプラグインがうまく動かなくなったりするので、
よくREADMEを読んで頭の中で処理を想像した時におかしくならないように気をつけます。
インプット
これらを使う前提でCSSを書いてみます。
@charset "utf-8"; .u-bounce { animation: bounce; } @prefix block; .box { color: color($color.font l(+10)); border: 1px solid @color; } @media screen and (min-width: $size.ipadpro) { .box { lost-center: $width; } } .image { width: width(500x403.jpg); height: height(500x403.jpg); } .card { lost-column: 1/2 no-flex; } @media screen and (min-width: $size.ipadpro) { .card { lost-column: 1/4 no-flex; } } .input:not(.not1, .not2) { transition: .3s easeInOutBack; border: 1px solid $color.quiet; } &:hover { border: 1px solid $color.accent; }
こんな感じになりました。後はビルドするだけです。
アウトプット
@charset "utf-8"; .u-bounce { -webkit-animation: bounce; animation: bounce; } @-webkit-keyframes bounce{ from, 20%, 53%, 80%, to{ -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); } 40%, 43%{ -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); -webkit-transform: translate3d(0, -30px, 0); transform: translate3d(0, -30px, 0); } 70%{ -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); -webkit-transform: translate3d(0, -15px, 0); transform: translate3d(0, -15px, 0); } 90%{ -webkit-transform: translate3d(0,-4px,0); transform: translate3d(0,-4px,0); } } @keyframes bounce{ from, 20%, 53%, 80%, to{ -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); } 40%, 43%{ -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); -webkit-transform: translate3d(0, -30px, 0); transform: translate3d(0, -30px, 0); } 70%{ -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); -webkit-transform: translate3d(0, -15px, 0); transform: translate3d(0, -15px, 0); } 90%{ -webkit-transform: translate3d(0,-4px,0); transform: translate3d(0,-4px,0); } } .block__box { color: rgb(92, 92, 92); border: 1px solid white; color: white; } .block__image { width: 500px; height: 403px; } .block__card { width: calc(99.99% * 1/2 - (30px - 30px * 1/2)); } .block__card:nth-child(n){ float: left; margin-right: 30px; clear: none; } .block__card:last-child{ margin-right: 0; } .block__card:nth-child(2n){ margin-right: 0; } .card:nth-child(2n + 1){ clear: left; } .block__input:not(.not1):not(.not2) { -webkit-transition: .3s cubic-bezier(0.68, -0.55, 0.265, 1.55); transition: .3s cubic-bezier(0.68, -0.55, 0.265, 1.55); border: 1px solid #b8b8b8; } .block__input:not(.not1):not(.not2):hover { border: 1px solid #EB7A77; } @media screen and (min-width: 1026px){ .block__box{ *zoom: 1; max-width: 960px; margin-left: auto; margin-right: auto; } .block__box:before{ content: ''; display: table; } .block__box:after{ content: ''; display: table; clear: both; } .block__card{ width: calc(99.99% * 1/4 - (30px - 30px * 1/4)); } .block__card:nth-child(n){ float: left; margin-right: 30px; clear: none; } .block__card:last-child{ margin-right: 0; } .block__card:nth-child(4n){ margin-right: 0; } .card:nth-child(4n + 1){ clear: left; } } /*# sourceMappingURL=style.css.map */
少ない記述量で、こんな感じに変換されました!
プラグイン一覧は最低でも週に1〜2個ずつ増えていっています。お気に入りのプラグインを探してみてください。
ちなみに、
postcss-namespaceと
postcss-preref、
postcss-cson-cssvarsは僕が自作してみたプラグインです。
思っていたほどプラグイン作成は難しいものではないので、 自分が欲しいプラグインが一覧に無ければ、似たプラグインで我慢せず自分で作ってしまうのも1つの手だと思います!
(p.s.)
この環境はpostcss-environment-nju33に置いておきました。