GulpとFLOCSSで始めるモバイルファースト的なCSS設計

hiloki/flocss: CSS organization methodology.

WebでフロントをされているみなさんはCSSを書いていて命名規則が面倒、何かまんじりとしない、そんなことはありませんでしょうか。私はあります。

CSSはより良く書くためにOOCSS、BEM、SMACCS、MCSSなど様々な考え方があります。そんな中でFLOCSSというOOCSS、BEM、SMACCS、MCSSなどのいいとこ取りをした設計を取り入れてみることにしました。

私は現在Gulpを使っているので、FLOCSSの考えをGulpで利用し、さらに噂のモバイルファースト的な記述を行ってみました。

FLOCSS(フロックス)とは

FLOCSSはOOCSS、BEM、SMACCS、MCSSなどの既存のコンセプトを取り入れたCSSの設計案で、命名規則はBEMの設計思想を元にしたMindBEMdingを採用し、基本的に3つの大レイヤーと、そのうちの一つであるObjectが持つ3つの子レイヤーで構成されます。

FLOCSSの基本レイヤー構成

  • Foundation
  • Layout
  • Object
    • Component
    • Project
    • Utility

レイヤーの役割

各レイヤーの役割は下記のようになります。

Foundation

Reset.cssを用いた初期化や、Base.cssなどのプロジェクトにおける基本的なスタイル。

/* Reset.css */

html,body {
  margin: 0;
  padding: 0;
}
/* Base.css */

html {
  font-size: 62.5%;
}

body {
  font-size: 1.6rem;
  text-size-adjust: 100%;
}

Layout

ヘッダーやフッター、mainのコンテンツエリア、サイドバーといった共通な大枠のスタイル。

.l-header {
  background: gray;
}

.l-footer {
  background: red;
}

Object

プロジェクトにおいて繰り返されるビジュアルパターンのスタイル。Component、Project、Utilityなどのレイヤーを内部にもつ。

Component

最低限の機能を持ったものとして定義される機能単位で分割したモジュールのスタイル。固有の幅や高さ、色などの指定はできるだけ避ける。BootstrapのComponentsにあるbuttonなど、他のProjectでも利用できるようなスタイルが該当する。

.c-btn {
   cursor: pointer;
 }

果物を例とした場合、りんご、みかん特有の色がProject、分量(g)、カロリー(kcal)がComponentとなる。色は個別で定義され、分量やカロリーは共通のComponentと定義できる。

Project

Project固有のパターンで、Componentに該当しない要素によって構成されるスタイル。例としては記事の一覧やユーザーのプロフィール、画像ギャラリーなど、そのProject特有のスタイルが該当する。

.p-article {
  border: 1px solid #CCC;
  background: #EFEFEF;
}

.p-article__title {
  font-size: 20px;
}
Utility

Component、Projectで解決することが難しい、または適切では無いといったような調整のためのスタイル。

.u-mbs {
  margin-bottom: 5px;
}

.u-mbm {
  margin-bottom: 20px;
}

.u-mbl {
  margin-bottom: 50px;
}

.u-cmove {
  text-align: center;
}

.u-rmove {
  text-align: right;
}

.u-lmove {
  text-align: left;
}

命名規則はBEMの設計思想を元にしたMindBEMding

FLOCSSでの命名規則はMindBEMdingを採用しています。BEMという設計思想を元にした命名規則で、モジュールをBlock、Element、Modifierといったように切り分け、「.Block__Element--Modifier」などとして記述します。

Projectを例にした場合のMindBEMding

<article class="p-articler">
   <h1 class="p-article__title">タイトル</h1>
    <time class="p-article__time">2XXX/XX/XX</time>
    <p class="p-article__text">テキスト</p>
</article>

Projectを例にしたModifierがある場合のMindBEMding

<article class="p-article p-article--another">
    <h1 class="p-article__title p-article__title--another">タイトル</h1>
    <time class="p-article__time p-article__time--another">2XXX/XX/XX</time>
    <p class="p-article__text p-article__text--another">テキスト</p>
</article>

--another」は「p-article」の幅や色違いのデザインがあった場合につけます。名称は任意です。

ただ上記のように、「block__element--modifier」に則っとり余りにもクラスが多くなる場合は、FLOCSSの定義には反しますが、下記のように子要素にスタイルを適用する子セレクタの利用を検討します。

.p-article--another > .p-article__title {}

モジュールに対しプレフィックス(接頭辞)をつける

FLOCSSでは役割を明確にするためにプレフィックスをつけます。

  • Layout:.l-*
  • Component:.c-*
  • Project:.p-*
  • Utility:.u-*

また、Modifierの命名の派生パターンとしてJavaScriptで操作される「状態」を表すようなModifierについては、「.is-*」とつけます。例としてはカレントページでナビゲーションの色を変えるための「.is-active」、といったようになります。

FLOCSSでのCSSファイルとディレクトリの構成

FLOCSSのレイヤー構成に従った場合、例としてCSSファイルは下記のようになります。

CSS

/* ==========================================================================
   Foundation
   ========================================================================== */

/* Reset
   ----------------------------------------------------------------- */
 
html,body {
    margin: 0;
    padding: 0;
}
 
/* Base
   ----------------------------------------------------------------- */
 
body {
    font-size: 16px;
}

/* ==========================================================================
   Layout
   ========================================================================== */
 
/* Header
   ----------------------------------------------------------------- */
 
.l-header {

}
 
/* Main
   ----------------------------------------------------------------- */
 
.l-main {

}

/* ==========================================================================
   Object
   ========================================================================== */

/* Component
   ----------------------------------------------------------------- */
 
/**
 * Button
 *
 * Blah Blah Blah
 */
 
.c-button {
}
 
/**
 * Media
 */

.c-media {
}

/* Project
   ----------------------------------------------------------------- */
 
/**
 * Articles
 */

.p-article {

}

.p-article__title {

}

/* Utitlity
   ----------------------------------------------------------------- */
 
/**
 * Clearfix
 */

.u-clearfix {

}
 
/**
 * Display
 */

.u-block {

}
 
/**
 * Margin
 */

.u-mb10 {
  margin-bottom: 10px;
}

Sassでのディレクトリ構成

Sassのようなプリプロセッサでファイル結合ができる場合、例として下記のようにレイヤーの役割を分割して管理することができます。

style.scss
├── foundation
│   ├── _base.scss
│   └── _reset.scss
├── layout
│   ├── _footer.scss
│   ├── _header.scss
│   ├── _main.scss
│   └── _sidebar.scss
└── object
    ├── component
    │   └── _button.scss
    ├── project
    │   ├── _article.scss
    │   ├── _gallery.scss
    │   └── _profile.scss
    └── utility
        ├── _clearfix.scss
        └── _margin.scss

GulpでSassコーディングを行えるようにする

今回はレイヤーの役割を分割して各ファイルに持たせる前提として進めていきます。gulpでSassコーディングを行うために、下記のコマンドを入力してプラグインをインストールします。

npm i -D gulp gulp-sass gulp-postcss

gulp-sassはgulpでSassのコンパイルを行うnpmパッケージ、gulp-postcssは各種プラグインを用いてCSSを変換するポストプロセッサーです。自動でベンダープレフィックスを付与してくれるAutoprefixerが利用できるのでおすすめです。

次に大量のimportを防ぐためにgulp-sass-globをインストールします。

npm i -D gulp-sass-glob

foundationフォルダに_a.scss、_b.scssという二つのファイルがあると仮定すると、通常、読み込む場合は下記のように記述します。

@import "foundation/_a.scss";
@import "foundation/_b.scss";

gulp-sass-globをインストールすると下記の記述で、foundationフォルダ下にあるファイルをすべて読むことができます。

@import "foundation/**";

gulpfile.jsは下記のように記述しました。

/*-------------------- plug-in --------------------------------- */
var gulp = require("gulp");
var sass = require("gulp-sass");
var postcss = require("gulp-postcss");
var sassGlob = require("gulp-sass-glob");
/*-------------------- plug-in --------------------------------- */

/*-------------------- タスク ---------------------------------- */
//scssをコンパイル
var paths = {
  "scss": "src/scss/", //作業するscssのフォルダ
  "css": "dest/css/"  //コンパイルして保存するcssのフォルダ
}
gulp.task('scss', function() {
  return gulp.src(paths.scss + '**/*.scss')
  .pipe(sassGlob({
      ignorePaths: [
        'foundation/_reset.scss'
      ]
  }))
  .pipe(sass({outputStyle: "expanded"}))
  .pipe(gulp.dest(paths.css))
});
/*-------------------- /タスク --------------------------------- */

gulp-sass-globはフォルダ下のファイルをすべて読み込むことができますが、デフォルトでは読み込みの順番はアルファベット順となるので、ignorePathsで一番初めに読み込んで欲しい「_reset.scss」を記述して読み込む順番を変える準備をしています。

今回はsrc下のscssフォルダ内に、下記のようにファイルを分割して設置しました。

scssファイルとディレクトリ構成
scssファイルとディレクトリ構成

「style.scss」にFLOCSSで構成したファイルを下記のように読み込みます。

@charset "utf-8";

/* ==========================================================================
 Foundation
============================================================================*/

@import "foundation/_reset.scss";
@import "foundation/**";

/* ==========================================================================
 layout
============================================================================*/

@import "layout/**";

/* ==========================================================================
 object
============================================================================*/

@import "object/**";

/* EoF */

「@import “foundation/**”;」の前に「@import “foundation/_reset.scss”;」と記述して、一番初めに「_reset.scss」を読み込むようにしています。gulpfile.jsで記述したignorePathsがここで生きてきます。ignorePathsは本来は読み込みを除外する効果があるのですが、一旦ignorePathsでfoundation内の「_reset.scss」を除外してから結合先の「style.scss」で記述することによって、順番を一部手動で変更しています。他はgulp-sass-globで一気に読み込んでいます。

モバイルファーストでスタイルを記述する

準備が整ったので記述を行っていきます。まず、headerに「.l-header」というクラスを記述します。

<header class="l-header">
ヘッダー
</header>

「.l-header」の「.l」は「Layout:.l-*」に乗っ取り記述しています。ヘッダーはlayoutに属するので、「layout/_header.scss」に下記のようにscss記法で記述をします。

/* Header
   ----------------------------------------------------------------- */

.l-header {

  background: red;

  @media all and (orientation: landscape) {
    background: green;
  }

  @media all and (min-width: 768px) {

    background: blue;

    @media all and (orientation: landscape) {
      background: yellow;
    }

  }

  @media all and (min-width: 1025px) {
    background: #EFEFEF;
  }

}

「background: red;」の範囲はスマホ、「background: green;」はスマホでの横向き、「background: blue;」はタブレット、「background: yellow;」はタブレットの横向き、「background: #EFEFEF;」はPCでの背景色となります。

モバイル用のスタイルを先に記述することによって無駄なスタイルを抑えることができ、ビジュアル表示の速度向上が見込めます。CSSの出力は下記のようになります。

.l-header {
  background: red;
}

@media all and (orientation: landscape) {
  .l-header {
    background: green;
  }
}

@media all and (min-width: 768px) {
  .l-header {
    background: blue;
  }
}

@media all and (min-width: 768px) and (orientation: landscape) {
  .l-header {
    background: yellow;
  }
}

@media all and (min-width: 1025px) {
  .l-header {
    background: #EFEFEF;
  }
}

今回は行ってはいませんが、メディアクエリはmixinなどにしてまとめておくことをおすすめします。mixinファイルを作った場合はfoundationに設置します。各場所でインクルードされることを考慮した場合、大レイヤーであるFoundationで扱うのが自然です。

多くなったメディアクエリをまとめる

メディアクエリをネストした場合、クエリごとに分ける場合と違い、記述が要素に身近になるので、要素ごとに見るべき箇所を移動するといったことを防ぐことができます。ですが、メディアクエリの記述がいたるところに出てくるので、スタイルが肥大化します。

そこでメディアクエリをまとめてくれる「node-css-mqpacker」プラグインを使います。node-css-mqpackerを利用するために、下記のコマンドでプラグインをインストールします。

npm i -D css-mqpacker

「css-mqpacker」を使えるようにgulpfile.jsに「css-mqpacker」の記述を追加します。

/*-------------------- plug-in --------------------------------- */
var gulp = require("gulp");
var sass = require('gulp-sass');
var postcss = require("gulp-postcss");
var sassGlob = require("gulp-sass-glob");
/*-------------------- plug-in --------------------------------- */

/*-------------------- タスク ---------------------------------- */
//scssをコンパイル
var paths = {
  "scss": "src/scss/", //作業するscssのフォルダ
  "css": "dest/css/"  //コンパイルして保存するcssのフォルダ
}
gulp.task('scss', function() {
  return gulp.src(paths.scss + '**/*.scss')
  .pipe(sassGlob({
      ignorePaths: [
        'foundation/_reset.scss'
      ]
  }))
  .pipe(sass({outputStyle: "expanded"}))
 .pipe(postcss([require("css-mqpacker")])) //追加
  .pipe(gulp.dest(paths.css))
});
/*-------------------- /タスク --------------------------------- */

「_header.scss」と「_footer.scss」に下記のような記述がある状態でコンパイルをしてみます。

/* _header.scss */
.l-header 
  background: red;
}

@media all and (orientation: landscape) {
  .l-header {
    background: green;
  }
}

@media all and (min-width: 768px) {
  .l-header {
    background: blue;
  }
}

@media all and (min-width: 768px) and (orientation: landscape) {
  .l-header {
    background: yellow;
  }
}

@media all and (min-width: 1025px) {
  .l-header {
    background: #EFEFEF;
  }
}
/* _footer.scss */
.l-footer 
  background: red;
}

@media all and (orientation: landscape) {
  .l-footer {
    background: green;
  }
}

@media all and (min-width: 768px) {
  .l-footer {
    background: blue;
  }
}

@media all and (min-width: 768px) and (orientation: landscape) {
  .l-footer {
    background: yellow;
  }
}

@media all and (min-width: 1025px) {
  .l-footer {
    background: #EFEFEF;
  }
}

コンパイルで出力されたCSSを見ると、メディアクエリ別に記述がまとめられていることがわかります。

.l-footer {
    background: red;
}

.l-header {
    background: red;
}

@media all and (orientation: landscape){
  .l-footer{
    background: green;
  }
  .l-header{
    background: green;
  }
}

@media all and (min-width: 768px){
  .l-footer{
    background: blue;
  }
  .l-header{
    background: blue;
  }
}

@media all and (min-width: 768px) and (orientation: landscape){
  .l-footer{
    background: yellow;
  }
  .l-header{
    background: yellow;
  }
}

@media all and (min-width: 1024px){
  .l-footer{
    background: #EFEFEF;
  }
  .l-header{
    background: #EFEFEF;
  }
}

まとめ

  • FLOCSSはOOCSS、BEM、SMACCS、MCSSなどの既存のコンセプトを取り入れたCSSの設計案。
  • FLOCSSでは全体の効果を及ぼすスタイルは大レイヤーに、個別のスタイルは子レイヤーに記述する。
  • 役割を明確にするために各レイヤーのプレフィックスをつける。
  • クラスの命名規則はBEMに由来したMindBEMding。
  • モバイルファーストでスタイルを記述するには、スマホ、タブレット、PCの順番で記述する。

今まで命名規則やファイルの分割など、それほど気にしたことはなかったのですが、設計を意識することによって、多少なりともCSSの作成が楽になりました。

FLOCSSは設計案としては比較的学習コストが少ないとはいえ、理解するにはそれなりの時間がかかります。ですが、コンポーネントなどを意識することによってプロパティの分け方なども向上するので、試してみても損はないと思います。

今回の記事の作成やFLOCSSを学ぶにあたって、下記のサイトを参考にさせていただきました。

flocss - CSS organization methodology.
スポンサーリンク
記事のタイトルとURLをコピーする

シェアする

フォローする

この記事をお届けした
PC ウェブログの最新ニュース情報を、
いいねしてチェックしよう!