Projectレイヤーを使いこなす! – FLOCSSで始めるCSS設計

Projectレイヤーを使いこなす! – FLOCSSで始めるCSS設計

こんにちは、フロントエンドエンジニアの中村です。

FLOCSSで始めるCSS設計」第4回目となります。

過去回は以下になりますので、まだ読まれていない方はぜひこちらもよろしくお願いします。

さて、今回はFLOCSSの”Project”レイヤーについてお送りいたします。

Projectレイヤーとは

Hiroki tani氏(@hiloki)のドキュメントによるとこう書かれています。

Project
プロジェクト固有のパターンであり、いくつかのComponentと、それに該当しない要素によって構成されるものを定義します。
例えば、記事一覧や、ユーザープロフィール、画像ギャラリーなどコンテンツを構成する要素などが該当します。

hiloki/flocss: CSS organization methodology.

例として「記事一覧」「ユーザープロフィール」などが挙げられていますね。

それでは、具体的なサンプルを交えてお話しします。

例:著者プロフィール

ここで弊社ブログの「著者プロフィール」を見てみましょう
%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-13-12-15-09

これは、記事ページ内で使用されるオブジェクトです。

「いくつかのComponent」に値するのは以下の通りです。

  • ボタンコンポーネント
  • サムネイルコンポーネント
  • 汎用見出しコンポーネント(著者名)
  • 汎用テキストコンポーネント(役職・自己紹介)
  • SNSリンクコンポーネント

これらのコンポーネントは、自身がmarginpaddingを持っていないため、レイアウトの際、使用されるオブジェクト内で改めて記述する必要があります。つまり、ドキュメントでいうところの「プロジェクト固有のパターン」を設定する必要があるということです。

では、どのようなコードになっているかデモをごらんください。

See the Pen NRzXWW by nayu (@nayucolony) on CodePen.

(実物とは多少異なりますがご了承ください)

Componentに与えられた.c-プレフィクスのついたclassに加えて、そのプロジェクト固有で与えられる.p-プレフィクスのついたclassが与えられています。これが、「プロジェクト固有のパターン」に該当するclassです。言い換えるならば各コンポーネントが、.p-author内にある時にだけ与えられるスタイルを定義しています。

性質上、主にレイアウト絡みのCSSが多くなる傾向にあります。
最初からコンポーネントにmargin-left: 10pxなんて与えられていたら、使いづらくて仕方ありませんよね。これらのスタイルは、それぞれのプロジェクトで適切に与えられるべきものです。

レイヤー、モジュールのカスケーディング

カスケーディングって何?

CSSとは”Cascading Style Sheets”の略です。
“Cascading”とは”階段上に連続する滝”という意味です。

CSSは、HTMLに対して幾つもスタイルを指定できますね?

See the Pen font-color cascading sample by nayu (@nayucolony) on CodePen.

上記のサンプルでは、文字に対するスタイル指定を3回行っています。
3回のスタイルの内、どれを適用するのか選ぶプロセスをカスケーディングといいます。

基本的には、後から(下に)書いたものがどんどんスタイルを上書きしていきます。

FLOCSSでは、カスケーディングは原則禁止

ドキュメントにはこう書かれています。

原則として、モジュール間のカスケーディング、他のモジュールを親とするセレクタを用いたカスケーディングは禁止とします。

特に同一レイヤーにおけるモジュール間のカスケーディング、例えば、次のような複数のセレクタを用いたカスケーディングは好ましくありません。

  • このボタンコンポーネント、この警告ボックスの中にある時はサイズ変えたい
  • このコメント用のスタイル、記事ページで使う時は文字色変えたい

これらのようなことは日常茶飯事かと思います。

デザインにおいて、どこにいってもボタンの横には必ず20px空いている、なんてことはありません。
同じ役割、同じ構造を持つボタンであっても、TPOに応じて佇まいを変えるべきなのは周知の事実です。

だからといって下記のように書いてしまうのは最適解でしょうか?

// Component
.c-button {
  ...
}
.c-dialog .c-button {
  ...
}

// Project
.p-comments {
  ...
}
.p-articles .p-comments {
  ...
}

コンポーネントは、コンポーネント単独で独立して再利用できるという前提があります。
上記のような記述を重ねることで「あらゆるコンポーネント内で異なるスタイルを持つコンポーネント」になってしまいます。

「警告ボックスの中のボタンのスタイル、注釈ボックスの中でも使いたいな〜」となった時、上記の方法が許されていたならば、下記のようにしてしまうでしょう。

.c-notice {

 // .p-dialog .c-button に書いたスタイルと同じ内容

}

なぜなら、「あるコンポーネント内での特有のスタイル」は、そのコンポーネント内でのみ有効です。
他のコンポーネント内で使う場合は、再びそのコンポーネント内にスタイルを書き込まなければならないからです。

そして、規模が膨れ上がるに連れて収拾がつかなくなり、コンポーネントとは何だったのだろう…と途方にくれ、最終的には!importantなんかで無理やりスタイリングを始めてしまうことでしょう。
それを防ぐためにCSS設計があるというのに……

ただし、例外があります。

レイヤー間におけるカスケーディングは可

ドキュメントにはこう書かれています。

ProjectレイヤーがComponentレイヤーのモジュールを変更することは許容します。

Projectレイヤーとは、プロジェクト固有のパターンです。
そのためだけに、わざわざコンポーネントに派生スタイルを書くべきか?というと、必ずしもそうではないというのがFLOCSSの考え方です。
(もちろん、汎用性の高いスタイルであればComponentのModifierとすべきです!)

要は、「ここではmargin-left:20pxにしたいわ」なんて時にBlock__Element--ml20なんていちいち作るのは、野暮ったいということです。

カスケーディングのデメリット

例外として許容されているレイヤー間のカスケードですが、わざわざ禁止しているのはデメリットがあるからです。

詳細度が上がる

入れ子にすることで、詳細度があがります。
詳細度の管理が煩雑になることは、CSS設計の大敵です
スタイルを再び書き換えたければ、その詳細度に勝る詳細度で上書きをする必要があります。

  • もっと入れ子にする
  • IDを使う
  • !importantを使う
  • !importantを使う
  • !importantを使う
  • !importantを使う

おそらくこの記事を読んでいる方は、CSS設計を学びたくてこの記事を読んでいる人でしょう。
上記のリストについて「もうこんなことしたくない」と思って学んでいる人が多いかと思います。
その瞬間は「ここはこの中で確実に完結するから問題無い」と思っていても、人生と同じで何があるかわかりません。
突然のレイアウト変更に開いた口が塞がらなかったことが今まで何回あるか覚えていますか?

詳細度をなるべく均一に保っておくことが大切だということは、その身体が覚えているはずです。

詳細度があがるのを回避するには

詳細度の不要な上昇を回避できる場合もあります。

  • サンプルの様に、ProjectのElementとしてのclassを与える
  • ComponentにModifierとしてスタイルを組み込んでしまう

最初に書いたデモコードは、カスケーディングを行っていません。
カスケーディングを行うと下記のようなコードになります。

See the Pen 著者情報サンプル-カスケード by nayu (@nayucolony) on CodePen.

カスケーディングが有効な場合

カスケーディングの使用に関してはケースバイケースと言える場合もあります。

例えば、「Elementとしてのclass」をいくつも発行することで見通しの悪いソースになることが明白な時などは、親要素に.p-プレフィクスを持ったclassを与え、カスケーディングを行うことでスッキリとさせることもできます。

ただし、その場合は、子要素を使うなどしてできるだけ影響範囲を絞っていきましょう。
意図せずして影響したスタイリングを修正する時、!importantの影がちらつきはじめる人は少なくないはずです!

この記事を読んでいる人は、もうそんな考えから卒業したいはずです…よね?

まとめ

  • Projectレイヤーは、プロジェクト固有のスタイリングを行いたい時に使用する。
  • .p-プレフィクスをつける
  • .c-で定義したスタイルを書き換えたい時は、詳細度の上昇を防ぐため、.p-Block .c-Blockのようにカスケーディングせず.p-Block__Elementなどのクラスを与えるか、.c-Block__Element--Modifierのようにする。
  • 例外として、.p-Block__Elementをいちいち発行することで逆に煩雑になってしまうならば、カスケーディングを許容する

いかがだったでしょうか。

Componentで基礎的な基礎的なモジュールを作って、各ページにて様々なスタイル調整を行う、というのは基本的なことだと思います。
Componentを作ったものの「ああ、ここでは微妙に違う、使いまわせない、新しく作らなきゃ」なんて思う必要はありませんよ。
ただし、カスケーディングを行うべきなのか、ProjectのElementなのか、もしかしたらComponentのModifierにしておけば後々使うのではないか……?なんかはよく考えましょう。

この連載に関するご意見やご感想がありましたら、ぜひtwitter(@nayucolony)まで!

気に入っていただけましたら、Facebookでのシェア、いいね、Tweet、はてブなど、是非ともお願い致します!