HOME > TECHNOLOGY > 開発環境

CSSメタ言語はStylusがスタイリッシュ!【WEBデザイナーのためのMac強化計画 #7】

Mac強化計画⑦

CSSメタ言語のスタンダードは、言わずと知れたSCSSエスシーエスエスSASSサスですよね。

SCSSはCSSの知識の範囲で記述でき、CSSにない機能を提供してくれるので多くの人に受け入れられているのだと思います。
入れ子/変数/mixinといった部分は通常のCSSにないパワフルな機能だと思いますが、LPのようなページ数の少ないコンテンツではあまりそのパワーを引き出せません

わざわざコンパイルが必要なのか?と疑問に思うケースも少なくありません。
SCSSにしたからといってCSSとは別の管理の難しさにぶつかり、かえって見通しが悪くなるなど、保守性の面から言ってもそんなに利点は感じません。

ということで、私は指定されない限り、SCSSでCSSを描くことはありません。

stylus

基本的にはstylusを使います。

これは好みの問題ですので、万人におすすめするわけではありませんが、SCSSに比べタイプ量は体感2/3ぐらいまで減らせます。
つまり、確実にメリットがあると言えます。

タイプ量が減る一例ですが、@importにワイルドカードが使えます
SCSSはファイル分割が進むとimportの記述だけでも面倒ですよね。

個人的には日本でのユーザー数が少ないと言う以外にデメリットはないと思っているので、日々啓蒙活動に励んでいるところです。

前置きが長くなってしまいましたが、今回はstylusのインストールですのでこの辺にしておきます。

インストール

前回のpugのインストールで利用した環境を利用します。

$ yarn add -D gulp-stylus gulp-stylint gulp-sourcemaps gulp-postcss autoprefixer css-mqpacker gulp-csso gulp-if

pugの時に比べ少しパッケージが多いですね!
簡単に説明します。

gulp-stylus

こちらがGulp用のstylus本体です。

gulp-stylint

stylintはルールを決めてstylusを運用するときに役立ちます。
記述の統一統一されていない場合にエラーを出力してくれます。

gulp-sourcemaps

sourcemapsはデベロッパーツールで出力されたCSSを見たときに、対応するstylusを見つけるのに役立ちます。

gulp-postcss

GulpでPostCSSを利用するプラグインです。
PostCSSはCSSの困りごとを解決するツールとして開発されています。
今回紹介する環境ではCSSにベンダープレフィックスを付けたり、メディアクエリーをまとめたりするのに利用しています。

autoprefixer

autoprefixerは文字通りベンダープレフィックスを自動で付与するために使用します。
PostCSSのプラグインとしてベンダープレフィックスを処理します。

css-mqpacker

mqpackerは散らばったメディアクエリー(@media)をまとめるために使用します。
こちらもPostCSSのプラグインとして処理します。

gulp-csso

重複するスタイルを統合するなど、リリース時のオプティマイズ(整理/最適化/圧縮)などの処理をします。

gulp-if

Gulpのタスクストリーム内で条件分岐させるのに必要です。
開発時とリリース時の処理を分岐して、開発中のデバッグをスムーズにします。

ファイル構成

プロジェクトルートに移動し、stylusのソースファイルを置くディレクトリを作成します。
その中にstyle.stylとassets/_reset.styl、assets/_font.styl、components/_flex.stylというファイルを設置します。

src/
  .stylintrc
  stylus/
    style.styl
    assets/
      _reset.styl
      _font.styl
    components
      _flex.styl

タスクの作成

gulpfile.jsを編集します。
前回のpugのインストールで利用したファイルに追記します。

const gulp = require('gulp');
const server = require('browser-sync');
const plumber = require('gulp-plumber');
const filter = require('gulp-filter');
const sourcemaps = require('gulp-sourcemaps');
const gulpif = require('gulp-if');
const pug = require('gulp-pug');
const beautify = require('gulp-html-beautify');
const stylus = require('gulp-stylus');
const stylint = require('gulp-stylint');
const postcss = require('gulp-postcss');
const csso = require('gulp-csso');

// ▽ ミニファイの設定
let minify = false;

// ▽ パスの設定
const src = 'src';
const dist = 'dist';
const path = {
  pug: {
    entry: [src+'/pug/**/*.pug']
  },
  stylus: {
    entry: [src+'/stylus/**/*.styl']
  }
}

// ▽ pugのコンパイル
function pugCompile() {
  return gulp
    .src(path.pug.entry)
    .pipe(plumber())
    .pipe(pug({pretty: true}))
    .pipe(filter(function(file){return !/\/_/.test(file.path) && !/^_/.test(file.relative);}))
    .pipe(beautify({'indent_size': 2, 'indent_char': ' '}))    
    .pipe(gulp.dest(dist));
}

// ▽ stylusのコンパイル
function stylusCompile() {
  return gulp
    .src(path.stylus.entry)
    .pipe(plumber())
    .pipe(gulpif(!minify, sourcemaps.init()))
    .pipe(stylint())
    .pipe(stylint.reporter())
    .pipe(stylus())
    .pipe(postcss([
      require('autoprefixer')({cascade: false}),
      require('css-mqpacker')
    ]))
    .pipe(filter(function(file){return !/\/_/.test(file.path) && !/^_/.test(file.relative);}))
    .pipe(gulpif(minify, csso()))
    .pipe(gulpif(!minify, sourcemaps.write()))
    .pipe(gulp.dest(dist+'/css'));
}

// ▽ サーバーをリロード(browser-sync)
function browserReload(done){
  server.reload();
  done();
}

// ▽ ライブリロードサーバー(browser-sync)
function serverInit(done) {
  server.init({
    server: {baseDir: 'dist'}
  });
  done();
}

// ▽ ファイルを監視
function watchFile(done) {
  gulp.watch(path.pug.entry).on('change', gulp.series(pugCompile, browserReload));
  gulp.watch(path.stylus.entry).on('change', gulp.series(stylusCompile, browserReload));
}

// ▽ Gulpにタスクを登録
gulp.task('sync', serverInit);
gulp.task('watch', watchFile);

続いて.stylintrcを編集します。
.stylintrcはstylusの文法が正しいかどうかをチェックする際の基準です。

{
  "blocks": false,
  "brackets": "never",
  "colons": "never",
  "semicolons": "never"
}

これでpugのコンパイルに加え、StylusをCSSに変換できるようになりました!

stylusをコンパイルしてみよう!

画面のスタイリングをしていきましょう。
先程作成した/src/stylus/style.stylを編集します。

@import 'assets/*'
body
  color #fff
  background-color #444
@import 'components/_flex'
@media screen and (max-width:480px)
  body
    color #333
    background-color #fff

続いて/src/stylus/assets/の中のファイルを編集します。
以下2つのファイルは@import ‘assets/*’の部分で、ワイルドカードで読み込んでいるファイルです。

*
  box-sizing border-box

html
  -webkit-tap-highlight-color transparent
  -webkit-font-smoothing antialiased
  -moz-osx-font-smoothing grayscale
html
  font-family -apple-system, BlinkMacSystemFont, "Helvetica Neue", Verdana, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, sans-serif

最後に/src/stylus/components/_flex.stylを編集します。
こちらは@import ‘components/_flex’の部分で読み込んでいます。

.flex
  display block
@media screen and (max-width:480px)
  .flex
    display block

HTMLにCSSを適用する必要がありますので、/src/pug/index.pugも修正しておきます。

- var TITLE = "agrius 佐藤農園 pugのコンパイルテスト"

doctype html
html(lang="ja")
  head
    include assets/_head
    link(rel="stylesheet", type="text/css", href="css/style.css")
  body
    .flex
      p パグが正常にできるかのテストです。
      p 1
      p 上の数字を変更してリロードされればOKです!

ターミナルからタスクを起動します。

$ yarn dev

ブラウザが起動して以下内容が表示されると思います。

パグが正常にできるかのテストです。
1
上の数字を変更してリロードされればOKです!

/src/stylus/components/_flex.stylを以下のように修正します。

.flex
  display flex
@media screen and (max-width:480px)
  .flex
    display block

ブラウザの表示が

パグが正常にできるかのテストです。1上の数字を変更してリロードされればOKです!

のように一行になれば成功です。

開発とリリースの切り替え

stylus環境構築がpugに比べパッケージが多かったのは、開発とリリースの切り替えたり、人の手では困難なCSSの最適化を自動で実行するためです。

pugはデバッグがほとんど必要としません。
かたやstylusはしっかりとしたデバッグ環境を整えておかないと、開発コストがどんどん増えていきます。
どの要素に何のスタイルあたっていて、それがどのsyylusに対応するかを把握するのはブラウザの開発パネル上だと非常に煩雑です。
それを解決するために、ソースマップという仕組みを利用します。

ソースマップは開発時にのみ利用するコードで、実際のリリース用CSSでは全く必要のないコードです。
そのため、リリース用ファイルはソースマップコードを取り除いた状態のCSSをアップロードする必要があります。
今回の環境の場合、条件分岐でその切り替えができるようにしてあります。

具体的にはgulpfile.js内の以下の部分で設定します。

// ▽ ミニファイの設定(開発時はfalseに設定)
let minify = false;
// ▽ ミニファイの設定(リリース時にはtrueに設定)
let minify = true;

さらにリリース時にはcssoというパッケージを利用して、CSSをオプティマイズ(整理/最適化/圧縮)処理しています。

今回のソースは敢えてhtmlのスタイルを2個所に分けて書いてあります。
cssoを適応前のCSSは以下のようになります。

html {
  font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Verdana, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, sans-serif;
}
* {
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
}
html {
  -webkit-tap-highlight-color: transparent;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
body {
  color: #fff;
  background-color: #444;
}
.flex {
  display: block;
}
@media screen and (max-width: 480px) {
  .flex {
    display: block;
  }
  body {
    color: #333;
    background-color: #fff;
  }
}
/* ここにソースマップが入ります! */

htmlのスタイルが2個所あることがわかります。
csso適応後は以下のようになります。

html{font-family:-apple-system,BlinkMacSystemFont,"Helvetica Neue",Verdana,"Hiragino Sans","Hiragino Kaku Gothic ProN",Meiryo,sans-serif;-webkit-tap-highlight-color:transparent;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*{-webkit-box-sizing:border-box;box-sizing:border-box}body{color:#fff;background-color:#444}.flex{display:block}@media screen and (max-width:480px){.flex{display:block}body{color:#333;background-color:#fff}}

見事に1行になりました。

このファイル内でhtmlを検索してみてください。

1つしかありませんよね!

cssoはこのように重複や短くできる部分を調整して自動的に再構築してくれます。

このように、リリースと開発を分けるのはプロのWEBデザイナーの常識となっていることも理解しておきましょう。

おまけ

ちなみにSCSSでもコンパイルできるようにするには、以下のようにしておけば上で紹介したstylusと同じ要領でコーディングできます。

追加パッケージをインストール

$ yarn add -D gulp-sass

gulpfile.jsに以下を追記

const sass = require('gulp-sass');
const scsslint = require('gulp-scss-lint');

// ▽ パスの設定 -> 追記はSCSSの部分のみでOK
const path = {
  pug: {
    entry: [src+'/pug/**/*.pug']
  },
  stylus: {
    entry: [src+'/stylus/**/*.styl']
  },
  scss: {
    entry: [src+'/scss/**/*.scss']
  }
}

// ▽ scssのコンパイル
function scssCompile() {
  return gulp
    .src(path.scss.entry)
    .pipe(plumber())
    .pipe(gulpif(!minify, sourcemaps.init()))
    .pipe(scsslint({config: ".scss-lint.yml"}))
    .pipe(sass({outputStyle: 'expanded'}))
    .pipe(postcss([
      require('autoprefixer')({cascade: false}),
      require('css-mqpacker')
    ]))
    .pipe(filter(function(file){return !/\/_/.test(file.path) && !/^_/.test(file.relative);}))
    .pipe(gulpif(minify, csso()))
    .pipe(gulpif(!minify, sourcemaps.write()))
    .pipe(gulp.dest(dist+'/css'));
}

// ▽ ファイルを監視 -> 追記はSCSSの部分のみでOK
function watchFile(done) {
  gulp.watch(path.pug.entry).on('change', gulp.series(pugCompile, browserReload));
  gulp.watch(path.stylus.entry).on('change', gulp.series(stylusCompile, browserReload));
  gulp.watch(path.scss.entry).on('change', gulp.series(scssCompile, browserReload));
}

SCSSのlintについては、プロジェクトルートに.scss-lint.ymlというファイルを用意して設定します。
あくまで参考ですが、下記しておきますね。
自分のスタイルで定義してみてください。

linters:
  BangFormat:
    enabled: true
    space_before_bang: true

  ColorKeyword:
    enabled: true

  ColorVariable:
    enabled: false

  Comment:
    enabled: true

  DebugStatement:
    enabled: true

  DeclarationOrder:
    enabled: true

  DuplicateProperty:
    enabled: true

  EmptyLineBetweenBlocks:
    enabled: true

  EmptyRule:
    enabled: true

  FinalNewline:
    enabled: false

  HexLength:
    enabled: true
    style: short

  HexNotation:
    enabled: true
    style: lowercase

  HexValidation:
    enabled: true

  Indentation:
    enabled: true
    allow_non_nested_indentation: false
    character: space
    width: 2

  PropertySortOrder:
    enabled: false

  Shorthand:
    enabled: true

  SingleLinePerProperty:
    enabled: true

  SingleLinePerSelector:
    enabled: true

  SpaceAfterComma:
    enabled: true

  SpaceAfterPropertyColon:
    enabled: true
    style: one_space

  SpaceAfterPropertyName:
    enabled: true

  SpaceBeforeBrace:
    enabled: true
    style: space

  SpaceBetweenParens:
    enabled: true
    spaces: 0

  TrailingSemicolon:
    enabled: true

  TrailingZero:
    enabled: true

  UnnecessaryMantissa:
    enabled: true

  ZeroUnit:
    enabled: true

これでstylusでもSCSSでも好きな方でCSSをコンパイルできるようになります。


これであなたはモダンなWEBデザイナーの仲間入りです!

HTMLとCSSがコンパイルできるようになりました。
後はデザインさえあれば、ガシガシWEBデザインを進めることができます。

一度環境さえ構築してしまえば、案件にようってちょっとカスタマイズするだけで使えるようになります。

プロのWEBデザイナーがどういった方法で品質を上げているのか少しでも理解し、参考にしていただけたら幸いです。


田畑を耕しながら田舎でのんびりWEB開発?
若い頃から準備すれば誰にでもできますよ!
今に疲れているクリエイターの方々の少しでも参考になれば嬉しいです。