GulpとSassとBEMみたいな何かで雑なCSS書くのをやめる

  • 7
    いいね
  • 0
    コメント

はじめに

こんにちは。Hamee Advent Calendar 2016 12日目の記事です。
去年も12日を担当しました。12は良い数字です。

今回はCSS設計、開発のお話です。

内容はタイトルの通りで、GulpやSass(SCSS)など使いますが、
その辺の説明は無限に良記事があるので端折ります。

あと、Gulp疲れたしnpm scripts使うよおじさん達には無縁の記事かもしれません。

下準備(知識&作業)

  • BEMの基本を押さえておく
  • Sass(SCSS)ちょっと勉強しておく
  • Gulpのインストールしておく
  • gulpfile.js書いとく

BEMの記法について

BEMは書き方がキモいから嫌だ。無理。という人が多い(私の周りのエンジニアもみんなキモいって言う)

class名がキモいことは否定しないけど私はブロックの名前が決まればあとは芋づる式に小要素のclass名が決まる「書きやすさ」やCSSを拡張したメタ言語を使っていれば「なんだかんだで見た目が綺麗になる」点が気に入っています。

とはいえ、.Block__Element--Modifierの形を小一時間眺めて
アンスコとハイフン2個ずつはいらんよなぁと正直思ったので以下のようにします。

  • .Block_Element-Modifier

まだ救いが足りない。
更にアンスコとハイフン混じりを見ると死んでしまう勢力を救うために以下のようにします。

  • .Block_Element.Modifier

この場合、マルチクラスで扱うこと前提となり、「.Modifierは単体では使用不可」などという
余計なルールが必要になりますがhtml側に書くclass名の冗長さを回避することはできます。

また、Sassを使ってコンポーネントをしっかり作っていれば、.Block_Elementに対応する
.Modifierの検索も容易にできるはずなのでエンジニアが絡むような大規模案件でも、大丈夫だろうと私は思っています。

BEMを褒めておきながらそのままのBEMを使う気は全くない

お前は作者の気持ちを理解していない!などと言われそうですが
こういうのはチーム内とかでのルール決めでみんなが頷けば問題ないと思っています。
今回は私の個人プレーなのでこれでいきます。大きな穴があったら教えてください...。

gulpfileについて

一応置いておきます。リネームしてプレフィックス付けて圧縮して、みたいなことしてます。

gulpfile.js
'use strict';

var gulp         = require('gulp');
var plumber      = require('gulp-plumber');
var rename       = require('gulp-rename');
var sass         = require('gulp-sass');
var autoPrefixer = require('gulp-autoprefixer');
var cssComb      = require('gulp-csscomb');
var cmq          = require('gulp-merge-media-queries');
var uglify       = require('gulp-uglify');
var concat       = require('gulp-concat');

gulp.task('sass',function(){
    gulp.src([
          'src/scss/**/*.scss',
          '!src/scss/**/_*.scss'
        ])
        .pipe(plumber({
            handleError: function (err) {
                console.log(err);
                this.emit('end');
            }
        }))
        .pipe(sass())
        .pipe(autoPrefixer())
        .pipe(cssComb())
        .pipe(cmq({log: true}))
        .pipe(concat('main.css'))
        .pipe(gulp.dest('dist/css'))
});

gulp.task('js',function(){
    gulp.src(['src/js/**/*.js'])
        .pipe(plumber({
            handleError: function (err) {
                console.log(err);
                this.emit('end');
            }
        }))
        .pipe(gulp.dest('dist/js'))
        .pipe(rename({
            suffix: '.min'
        }))
        .pipe(uglify())
        .pipe(gulp.dest('dist/js'))
});

gulp.task('default',function(){
    gulp.watch('src/js/**/*.js',['js']);
    gulp.watch('src/scss/**/*.scss',['sass']);
});

ディレクトリ構成

こんな感じでコンパイル先のdistディレクトリなどにscss以下が合体圧縮された
main.cssを生成するようにしておく。

src以下のassets(コンパイル前)

assets/
 ├ img/
 ├ js/
 └ scss/
   ├ lib/
   ├ base/
   ├ components/
   ├ main.scss(実際にコンパイルするのはこいつだけ)
   └ その他ページ固有のCSSとか/

吐き出し先のassets(コンパイル後)

assets/
 ├ img/
 ├ js/
 └ css/
   └ main.css

main.scssの中身

こんな感じでimport祭りをする。
実際のスタイルは各コンポーネントに記されていて、ここではそれを読み込むだけ。

main.scss
@charset "UTF-8";

// ===========================================
// library
// ===========================================
@import "./lib/library1"; // bootstrapとか入れるんだったらここで
@import "./lib/library2";
@import "./lib/library3";

// ===========================================
// base
// ===========================================
@import "./base/reset";
@import "./base/variables";

// ===========================================
// components
// ===========================================
// common
@import "./components/common/header";
@import "./components/common/footer";
@import "./components/common/utility";
@import "./components/common/label";

@import "./components/component1"; // 適切なコンポーネントの名前つけてね
@import "./components/component2";
@import "./components/component3";
@import "./components/component4";
@import "./components/component5";

componentsの中身

ヘッダーのような共通パーツであればcomponents/common/_header.scssのようにし、
どこでも利用出来るバーツであれば適切な名前を付けてcomponents/_component1.scssのように作ってやると良い。
特定のページ固有スタイル群があればよしなにディレクトリを作ってグループ分けすると更に良い。

例)_example.scss
.Example {
  &_head {
    font-size: 24px;
    font-weight: bold;
  }
  &_body {
    font-size: 18px;
    text-align: justify;
  }
  &_foot {
    font-size: 12px;
    text-align: right;
  }
}
html側
<div class="Example">
  <h2 class="Example_head">タイトル</h2>
  <p class="Example_body">本文</p>
  <hr>
  <p class="Example_foot">著者</p>
</div>

Modyfierつける場合はこんな感じ

例)新しい記事に赤いラベル付けるとか
.Example {
  &_head {
    font-size: 24px;
    font-weight: bold;
    &.new { // ここ
      border-left: 2px solid red; 
    }
  }
  &_body {
    font-size: 18px;
    text-align: justify;
  }
  &_foot {
    font-size: 12px;
    text-align: right;
  }
}
html側
<div class="Example">
  <h2 class="Example_head new">タイトル</h2>
  <p class="Example_body">本文</p>
  <hr>
  <p class="Example_foot">著者</p>
</div>

個人的には割と綺麗だと思っています。

おわりに

再利用できそうなコンポーネント作ったはいいけどそのパーツの位置はどうやって調整するか迷うと思いますが
レイアウトの調整は別のクラスを作っておくと良いと思います。

パーツ再利用しまくるような大規模サービスであれば具体的に
_util.scss_layout.scssみたいなものを作っておけば幸せになれそうな予感はしています。

ちょっと面倒ですが、こうでもしないと結局全部「あるページのここでしか使えない」パーツに成り下がってしまうので頑張る価値は十分にあるのではないでしょうか。

小さな改修があったときに道具箱の中にあるものを使ってちょちょいと修正するのは気持ちいいものです。
こういうのをどこまで頑張るかはそのサービスやページの寿命と相談といったところでしょうか。