Bourbon, Neat, Bitters, Refills をお手柔らかにお願いします

正直な所、この投稿に関しては「お手柔らかにお願いします」としか言えません…

Gulpでお手柔らかにお願いします

基本的な使い方は分かったにしても、現場で使うにはGulpなどに絡ませて使うのでないだろうか?という事で、悪戦苦闘してみた。

自分で書いてみたのでお手柔らかにお願いします

return to toc

dollplayer2501/practice_gulp_refills: My practice of using Gulp for Refills (Bourbon, Neat, Bitters)

This is my practice for which I use Refills (Bourbon, Neat, Bitters) with Gulp, Browsersync, Sass.

I made this for the following purposes

* I want to use Refills, but do not want to use too much Bourbon, Neat, Bitters.
* I want the environment which is automatically compiled Sass, concated Refills’s javascript and reloaded web browser.
* Now, I don’t need CoffeeScript, HTML template, image optimization, switching of development and production, deploying to server.

…という事で、以下はいやマジどうでもいいです。

Proteus – Gulp

return to toc

“Static site starter kits from your friends at thoughtbot” という事で、Proteusという本家プロジェクトが存在する。
これは、thoughtbot四兄弟をベースにしたMiddlemanJekyllのスターターキットという印象を受けたが、いずれにせよ自分には縁が無い気がしている一方で、Gulpをベースにしたスターターキットが Proteus – Gulp になる…という認識。

で、その Proteus – Gulp 、 package.jsondependencies を見て、まぁ確かにそうなんだろうけど満漢全席なイメージで、コレをそのまま使うには自分には荷が重い…という印象で。
自分がやりたいのはささやかなモノで…

  • Sassの自動コンパイル
  • Chromeでのデバッグに便利なCSSソースマップが欲しい
  • ベンダープレフィックスは流行りだしパイプしておいた方が良いと思うけど(そもそもがIE8はサポートされてないしなぁ…その辺りどうなるんだろ…)
  • Javascriptの結合と圧縮…CoffeeScriptは今はいいです…
  • HTMLは今は素のままでいいです…
  • 以上、上記の更新タイミングでブラウザの自動リロードは格好良さそう

例えば、本番時と開発時の切り替え、画像の最適化、HTMLテンプレートエンジンの使用など、とりあえず置いておいて、核となる基本的な部分は押さえておきたいと思い上記をアップした次第。

node-bourbon ・ node-neat は現時点(2016年9月)では微妙…

return to toc

Proteus – Gulppackage.json を見ると、 node-bourbonnode-neat を使っている。
…のだが当初、これらは何をするパッケージなのか良く分からなかった。
何となくだが、これらは実体?をパッケージ化して、直接 bourbon installneat install をせず、Gulpから includePaths を設定、Sass/Scssファイルで @import するだけでOKという認識。
なので、有用なのかどうなのか微妙な気がした。
とは言え、自分もこれらを直接触る事は無いだろうからと当初は使っていたのだが、微妙な問題が生じたため使用を断念、ただしこれらと同様の原理で使える node-normalize-scss は使用。

で、その微妙な問題が二点ほど。

第一点目は言いがかりに近いが、node-bourbonnode-neat 、更には自分の場合 node-normalize-scss を使いたいが、これらの includePaths が微妙な件。

以下、前述のGitHubにアップした構成に準拠して、例えば、下記の様な Gulpfile.js を実行してみた。

'use strict';
var gulp = require('gulp');
var sass = require('gulp-ruby-sass');
var normalize = require('node-normalize-scss');
var bourbon = require('node-bourbon');
var neat = require('node-neat');

gulp.task('testing', function() {
  console.log('--');
  console.log(normalize.includePaths);
  console.log('--');
  console.log(bourbon.includePaths);
  console.log('--');
  console.log(neat.includePaths);
  console.log('--');
  console.log(neat.includePaths.concat(normalize.includePaths));
  console.log('--');

  var a1 = neat.includePaths.concat(normalize.includePaths);
  var a2 = a1.join(',');
  var a3 = a2.split(',');
  console.log(a3);
});
$ gulp testing
[15:15:52] Working directory changed to ~/www/apache24/htdocs/TMP/test24
[15:15:52] Using gulpfile ~/www/apache24/htdocs/TMP/test24/Gulpfile.js
[15:15:52] Starting 'testing'...
--
/Users/HOGE/www/apache24/htdocs/TMP/test24/node_modules/node-normalize-scss
--
[ '/Users/HOGE/www/apache24/htdocs/TMP/test24/node_modules/bourbon/app/assets/stylesheets' ]
--
[ [ '/Users/HOGE/www/apache24/htdocs/TMP/test24/node_modules/bourbon/app/assets/stylesheets' ],
  '/Users/HOGE/www/apache24/htdocs/TMP/test24/node_modules/bourbon-neat/app/assets/stylesheets' ]
--
[ [ '/Users/HOGE/www/apache24/htdocs/TMP/test24/node_modules/bourbon/app/assets/stylesheets' ],
  '/Users/HOGE/www/apache24/htdocs/TMP/test24/node_modules/bourbon-neat/app/assets/stylesheets',
  '/Users/HOGE/www/apache24/htdocs/TMP/test24/node_modules/node-normalize-scss' ]
--
[ '/Users/HOGE/www/apache24/htdocs/TMP/test24/node_modules/bourbon/app/assets/stylesheets',
  '/Users/HOGE/www/apache24/htdocs/TMP/test24/node_modules/bourbon-neat/app/assets/stylesheets',
  '/Users/HOGE/www/apache24/htdocs/TMP/test24/node_modules/node-normalize-scss' ]
[15:15:52] Finished 'testing' after 2.73 ms

まず、 neat.includePathsbourbon.includePaths に包含されている、と言っても良い気がするのだが、配列の階層が違うと言うか…
正直、スキルも低いし妙案も思い付かなかったしてで Gulpfile.js 後半の様な joinsplit するという。

故に、Sassコンパイルは下記の様になるのか…

'use strict';
var gulp = require('gulp');
var sass = require('gulp-ruby-sass');
var normalize = require('node-normalize-scss');
var bourbon = require('node-bourbon');
var neat = require('node-neat');

gulp.task('stylesheets', function () {
  return sass([
      './_source/assets/stylesheets/application.scss'
    ], {
      style: 'expanded',
      loadPath: neat.includePaths.concat(normalize.includePaths).join(',').split(','),
      sourcemap: false,
      emitCompileError: true,
      compass: false
    })
      .on('error', sass.logError)
    .pipe(gulp.dest('./HTML/assets/stylesheets/'));
});

そして第二点目の問題が発現してポキっと折れた…

$ gulp stylesheets
[15:38:40] Using gulpfile ~/www/apache24/htdocs/TMP/test24/Gulpfile.js
[15:38:40] Starting 'stylesheets'...
[15:38:42] error _source/assets/stylesheets/base/base/_variables.scss (Line 6: Undefined variable: "$font-stack-system".)
Error in plugin 'gulp-ruby-sass'
Message:
    Sass compilation failed. See console output for more information.
[15:38:42] Finished 'stylesheets' after 1.33 s

改めて .sass-cache/HTML/rm -rf した結果の ./HTML/assets/stylesheets/application.css が下記。

/*
Error: Undefined variable: "$font-stack-system".
        on line 6 of _source/assets/stylesheets/base/base/_variables.scss
        from line 6 of _source/assets/stylesheets/base/base/_base.scss
        from line 9 of ./_source/assets/stylesheets/application.scss

対処方法は「Rails と bourbon と neat と bitters – Qiita」にある通りで、「Font Stacks – Bourbon – Documentation」の変数が使える模様。

根本的な原因は、Gemとnpmで提供されるバージョンの差異かと。
前ページの通り、Gem版は Bourbon 5.0.0.beta.6Neat 1.8.0 、npm版は node_modules/ ディレクトリを掘って見ると Bourbon 4.2.7Neat 1.7.2 だった。
ネットで調べていて何かで見たけど、Gem版Bourbonが現在ベータ版なのでnpmに反映されていないのだか何だかで。

gulp-ruby-sass

return to toc

…と言うか…

…正直、何が違うのかと… gulp-ruby-sass はRubyのSassを掴んでいる、 node-sass は LibSass のラッパー、程度はさすがに分かるとして… gulp-sass はインストール時に node-sass もインストールしている模様…
Proteus – Gulp では gulp-ruby-sass を使っているにも関わらず、当初、コンパイル速度が早い、使い慣れている、Gulp の構文に違和感が無い、という理由から gulp-sass を使おうとしていた。

ところが、Refills の各コンポーネントを _source/assets/javascripts/modules/ に配置しても、 ACCORDION / TABS の様な @include を含むScssでコンパイルエラーが。

'use strict';
var gulp = require('gulp');
var normalize = require('node-normalize-scss');
var sass = require('gulp-sass');

gulp.task('sass', function() {
//gulp.src('./source/assets/stylesheets/**/*.scss')
  gulp.src('_source/assets/stylesheets/application.scss')
    .pipe(sass({
        includePaths: normalize.includePaths,
        outputStyle: 'expanded'
      }))
        .on('error', sass.logError)
    .pipe(gulp.dest('./HTML/assets/stylesheets'));
});

ではあるが、上記の Gulpfile.js の様に、ファイル名まで指定するとSassコンパイルは通る模様。
そう言えば node-sass ではワイルドカード指定ができるけど、これ、Gulp側で何をどうやってトップと言うかエントリポイントと言うか…今回の場合だと _source/assets/stylesheets/application.scss …を判断してるのだろうかと。
…という事を考えると、エントリポイントを明示的に記述した方が良い気もする一方で、今回は無かったが、複数のSassファイルを gulp-ruby-sass で指定する場合だが、多分下記。

gulp.task('stylesheets', function () {
  return sass([
        './file/to/path/css_1.scss',
        './file/to/path/css_2.scss',
      ], {
      :

ベンダープレフィックス・CSSソースマップ・圧縮

return to toc

ベンダープレフィックスを Proteus – Gulp では gulp-autoprefixer を使っているが、特に深い理由も無く何となく、 gulp-pleeease を使用。
ここでややこしくなるのが、CSSソースマップと圧縮をどのパッケージが担当するのだろうかという事で。
および、CSSを圧縮するとソースマップに不具合が生じる、みたいな記述を目にしたが…実際のトコロ、どうなんだろうかと。
なお、CSSソースマップは別ファイルではなくCSSファイルに追加。

'use strict';
var gulp = require('gulp');
var normalize = require('node-normalize-scss');
var sass = require('gulp-ruby-sass');
var pleeease = require('gulp-pleeease');
var sourcemaps = require('gulp-sourcemaps');

gulp.task('stylesheets', function () {
  return sass([
        './_source/assets/stylesheets/application.scss'
      ], {
        style: 'expanded',
        loadPath: [ normalize.includePaths ],
        sourcemap: true,     // **
        emitCompileError: true,
        compass: false
      })
        .on('error', sass.logError)
    .pipe(pleeease({
        autoprefixer: true,
        filters: true,
        rem: true,
        pseudoElements: true,
        opacity: true,
        import: true,
        minifier: false,     // **
        mqpacker: false,
        sourcemaps: false,
        next: false
      }))
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('./HTML/assets/stylesheets/'));
});

上記、sasssourcemap:false でソースマップを出力しない、 pleeeaseminifier:true でCSS圧縮。

node-sass だと下記になるのか…
未確認だがCSS圧縮は sassoutputStyle:'compressed' もしくは pleeeaseminifier:true 、ソースマップの出力切り替えは…不明…

'use strict';
var gulp = require('gulp');
var normalize = require('node-normalize-scss');
var sass = require('gulp-sass');
var sourcemaps = require('gulp-sourcemaps');
var pleeease = require('gulp-pleeease');

gulp.task('stylesheets', function () {
  return gulp.src([
        './_source/assets/stylesheets/application.scss'
      ])
    .pipe(sourcemaps.init())
    .pipe(sass({
        includePaths: normalize.includePaths,
        outputStyle: 'expanded' // 'compressed'
      })
        .on('error', sass.logError))
    .pipe(pleeease({
        autoprefixer: true,
        filters: true,
        rem: true,
        pseudoElements: true,
        opacity: true,
        import: true,
        minifier: false,         // **
        mqpacker: false,
        sourcemaps: false,
        next: false
      }))
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('./HTML/assets/stylesheets/'));
});

Browsersync をお手柔らかにお願いします

return to toc

BrowsersyncとSass・Gulpを絡ませた記述、かつ、日本語で読めるもの、かつ、数年前の記事…は、どうなのだろうか…
具体的には Sassで .pipe(browserSync.reload({stream:true})); だと、Sassコンパイルの終了を待たずにリロードしているのではないかと…
このため、本家の Browser Reloading – Browsersync + Gulp.js …これはJavascriptの場合の書き方になるが、これを参考に…

例えば自分の Gulpfile.js から、Sassコンパイル部分のみ下記に抜き出してみた。
この時の package.json によると、Browsersyncは 2.14.0 で、これは今後どうなるのか、ならないのか…本家情報をウォッチしてる訳では無いので何とも。
とは言え、多少回りくどい書き方になった気もするが、単発のSassコンパイルのみ( gulp stylesheets )にも、ブラウザ同期( gulp serve )にも対応するので、構造的にはこちらの方が好きかなぁと。
なお、Sassコンパイルでは直接ファイル名まで指定しているが、 gulp.watch ではワイルドカード指定している。

'use strict';
var gulp = require('gulp');
: 変更無し
var browserSync = require('browser-sync').create();

gulp.task('stylesheets', function () {
  : 変更無し
});

gulp.task('stylesheets-watch', ['stylesheets'], function (done) {
  browserSync.reload();
  done();
});

//gulp.task('serve', ['stylesheets', 'javascripts'], function () {
gulp.task('serve', ['stylesheets'], function () {
  browserSync.init({
    server: {
      baseDir: 'HTML'
    }
  });

  gulp.watch([
      './_source/assets/stylesheets/**/*.scss'
    ], ['stylesheets-watch']);
});

gulp.task('default', ['serve']);

実際どぉ?

return to toc

いやー何と言うか、ココに辿り着くだけで精一杯だったので…

確かに、Refillsのコンポーネントをただ貼り付けただけに近いGitHubにアップしたプレビュー程度だと、物凄く簡単にできる気がする。

そしてここから先、例えばテキストの文字色を変えたいなぁなど、当たり前だけど当然要望として出て来るが、そうなるとやはりまぁお察しで、少なくともどこかにある変数をチョイと変えれば一発で、みたいには行かない気がする。
そこで今回はRefillsのコンポーネントは一切修正せず、 ./_source/assets/stylesheets/_global.scss でオーバーライドしたが…
例えば Refillsの Empties Patterns というのもあるので Bourbon ・ Neat ・ Bitters のMixinを使い慣れている方には有用かと思うけど…自分にはまだ荷が重いので、フルスタックの記述をチョコチョコ修正する方が気が楽かなぁと。

くどいけど、本来は bourbon install などで生成されたファイルを直接書き換えるのがあるべき姿なんだろうねぇ…多分…と。
これ、どうにも抵抗があり、自分の感覚ではComposerで導入したPHPパッケージを直接書き換える、みたいな居心地の悪さを感じたけど、多分そうではなく、例えば wp scaffold _s でテーマをゴリゴリ書いていくのと同等だよーとか。
ただ…オリジナルをゴリゴリ修正して、ふとオリジナルを参照したくなった場合って…どこか別のディレクトリで install して差分比較すれば良いのかなぁとか?

次に、日本語フォントを扱う場合で。
Refillsにも Type Systems が用意されているが、もしかしたら独自のコンポーネント的な何かを準備する必要があるかなぁと。
例えば Bitters – Example の日本語版、更には各コンポーネントと連動させて、みたいな?

もう一つ、jQueryのライブラリ(& CSS)を組み込む場合…
GitHubにアップした作例ではページ内スクロールを組み込んだが、これはもうケースバイケースなので何とも…
とても細かい点を言えば、Javascript・CSSファイルをメインのJavascript・CSSファイルに結合するのか否か…個人的にはそれぞれで vendor ディレクトリに格納、みたいな配置になるかなぁと。

2016/09/01

Menu of this post

  1. 1. thoughtbot四兄弟をお手柔らかにお願いします
  2. 2. Gulpでお手柔らかにお願いします