★2022/04/19 更新
約3年FLOCSSを使い倒し体の一部となった私のためのFLOCSS、編集し直しました。
FLOCSSとは
「Foundation Layout Object CSS」
OOCSSやBEM、SMACSSのいいとこ取りをしたCSS設計思想。
構造
Foundation
サイト全体のデフォルトスタイルを管理。
┣ reset.scss CDNで読み込むようにしたよ
┣ base.scss
┣ variable.scss サイト全体で使える変数
┣ mixin サイト全体で使えるmixinを管理
Layout
各ページを構成するサイト全体で共通したエリアを管理。
┣ l-wrapper.scss
┣ l-header.scss
┣ l-main.scss
┣ l-footer.scss
Object
サイト全体で再利用できるパターンを持つオブジェクトを管理
┣ Component
小さな単位のオブジェクトを管理(ボタンなど)
┣ c-button.scss
┣ c-grid.scss
┣ …
┣ Project
いくつかのComponentと、他の要素によって構成される大きな単位のオブジェクトを管理
┣ p-card.scss
┣ p-profile.scss
┣ ...
┣ Utility
ComponentとProjectのモディファイア(パターン)で解決することができないスタイル、また、調整のための便利クラスなどを管理。
┣ u-utility.scss
┣ u-color.scss
┣ u-margin.scss
┣ u-padding.scss
┣ Theme
テーマによる色の切り替えなど、ページ単位の色違いとか
┣ t-blue.scss
┣ ...
Foundation
サイト全体のデフォルトスタイルを管理するディレクトリ。
reset.scss
ブラウザのデフォルトのスタイルをリセットするcssを定義。
私はdestyle.cssをCDNで読み込んで使用しています。(アプデにすぐ対応できるから)
デフォルトのスタイリングをほぼ無くすことができるリセットCSS
- box-sizing: border-box;が含まれている
- iOSでタップした時のグレー表示も消してくれる
- marginは基本的に0
base.scss
サイトを構成する上で、デザインの基本の下地、土台となるスタイルを定義
- 基本的にタグ自体にスタイルを定義すること。
-
reset.cssで足りなかったリセットを追加。
- 基本の文字サイズやフォントファミリーなどの追加。
Layout
各ページを構成する、ヘッダー、メインコンテンツエリア、コンテナ、フッターなどのレイアウトに関するスタイルをエリアごとに管理。
- 位置などレイアウトの指定のみ定義する
- 子要素はなるべく入れない
- 中身のデザインは
Projectで定義する
//ページ全体からみたレイアウトに対するスタイル
.l-header {
position: fixed;
top: 0;
left: 0;
z-index: 100;
width: 100%;
}
//ヘッダーというパーツのスタイル
.p-header {
display: flex;
&__logo{
width:100px;
height:50px;
...
}
...
}
l-wrapper.scss
bodyタグのすぐ下で、ヘッダーとメインエリアとフッターをかこむ
l-header.scss
ヘッダーの位置のスタイルを定義
(
l-nav.scss)
navは画面構造によって.p-nav
として.l-header
のなかに入ることもあるが、.l-
という大枠のほうがやりやすければLayoutでOKとする。
l-main.scss
メインのコンテンツエリアの共通スタイルを定義
- ヘッダーのfixed分の相殺や余白など
- 共通の上下左右の余白などのみの定義
l-container.scss
サイトのメインエリア内のコンテンツ幅などの指定
l-footer.scss
フッター位置のスタイルを定義
Object
繰り返し色んな場所で使うパーツやブロックをすべてObjectと定義。
- 小さな単位のパーツを
Component に。
- Componentが集まったブロックや、大きなエリアは
Project に。
Component
色んな場所で使われてる小さな単位のパーツを管理。
- ネストの回数を決めておくと破綻しない
- marginなど余白は定義しない。あくまでパーツとして扱う
例)
c-button.scss
c-label.scss
c-input.scss
…
Project
いくつかの Component と要素によって構成される、大きなブロックやエリアを管理。
- 使い回さないブロックでも分けて管理したいときにも。
- 小さい単位のComponentを集めて、一つのオブジェクトとして扱いたい時
- Componentとするには大きすぎる時
例)
p-card.scss
p-first-view.scss
p-button-group.scss
…
<div class="p-card">
<div class="p-card__header">
</div>
<div class="p-card__body">
</div>
<div class="p-card__footer">
<a class="c-button" href="/"></a>
</div>
</div>
★ ComponentとProjectの違い
人間で言えば、裸んぼの人間が最小単位のモジュールcomponentで、他にネクタイや靴下、スーツといったcomponentがあり、組み合わせてビジネスマンprojectになると考えられます
(抜粋:FLOCSSを使ったCSS設計での悩みどころと解決案 - Qiita)
ここを読んでかなりしっくりきました。
- 小さい単位をComponent
- Componentの集まりをProject
★ flexboxのレイアウトシステムもコンポーネント化しておくと便利
Bootstrapのようなrow,colは、 Component として管理する。
layout で管理しない理由は各ページで必ず使うとは限らないから
Utility で管理しない理由は
Project と組み合わせて使用する場面もあるため。
<main class="l-main">
<div class="p-block c-grid">
<div class="p-block__item c-grid__col-6"></div>
<div class="p-block__item c-grid__col-6"></div>
</div>
</main>
管理画面のようなシンプルなコーディングでは大活躍した.c-grid
だが
デザイン性のあるサイトのコーディングでは、使いづらい。
さらにいえばdisplay:grid
を覚えてからは、.p-
で完結したほうが見返しやすいし、全く使わなくなった。
★ アイコン付きボタンは.p-button
と.c-button
を並列にする
<a class="p-button p-button--icon c-button" href="/">
<img src="icon-arrow.jpg" class="c-icon c-icon--arrow">
<span>お問い合わせ</span>
</a>
わかりやすくて気に入ってたんだけど、.c-icon
は.p-button
でしか使わないので、この方法はあまり使わなかった。(管理画面のコーディングでは使えるかも)
やってみたら.p-
にも.c-
にもbutton
がいるのはちょっとムズムズした。
<a class="c-button c-button--icon" href="/">
<img src="icon-arrow.jpg" class="c-button--icon__unit">
<span class="c-button--icon__text">お問い合わせ</span>
</a>
Utility
- Component、Projectのオブジェクトを無駄に増やしてしまうことを防ぐ。
- Projectとして定義するには要素が少なすぎる場合やCmponent単体で使うときに使用
オブジェクト自体が持つべきではないmarginの代わりに.u-mb10 { margin-bottom: 10px; }
のような隣接する要素との間隔をつくるといった役割。 - すべての余白
margin
をUtilityクラスでつけることはしない。 - あくまで補助としての役割であること。
u-margin.scss
u-padding.scss
u-color.scss
u-text.scss
...
↓↓↓↓↓↓↓ あなたの記事の内容
命名規則
───────
Component、ProjectのObjectを無駄に増やしてしまうことを防ぐ。
Object自体が持つべきではないmarginの代わりに.u-mb10 { margin-bottom: 10px; }
のような隣接するモジュールとの間隔をつくるといった役割。
↑↑↑↑↑↑↑ 編集リクエストの内容
プレフィックスをつける
layout なら
.l-
、 Componentなら
.c-
のように、属してるフォルダ名の{頭文字}
+-
でプレフィックスを頭につけ、ファイル名だけでもで判断しやすくする(Foundationはリセットや下地が目的なので除く)
l-header.scss
l-footer.scss
c-button.scss
p-card.scss
ファイル名 = クラス名
l-header.scss
には.l-header
というクラス名を定義
ファイル名、クラス名は省略しない
- 誰が見てもわかるようにファイル名、クラス名なるべくは省略しない
txt
やbtn
もNG。自分で言葉を作らないこと。
ただし、Utility
はクラス名が長くなりがちなので省略可とし、ファイル名は省略しないことで誰が見てもわかるようにする。
MindBEMdingの命名規則を採用
BEM命名規則とはBlock、Element、Modifierに分類して構成される規則。
(参考:BEMとMindBEMdingの命名規則の違い - コード日進月歩)
Block
- オブジェクトの名前。
- 誰が見ても分かる名前にすること。
例)aboutページで使うcardなら.p-about-card
のように
Modifier
- オブジェクトの色違いやサイズ違いは、色名やサイズ名を
--
でつなぎ.Block--Modifier
のようなかたちにする。
Modifierは原則2つまでとする
(.c-button--blue
,.c-button--small
など) -
.Block--Modifier--Modifier
はNG。
Modifierは原則2つまで
これは足りなかったのでそこまでこだわらないようにしていた。
あまりにも多くて分かりづらいというときは別のComponentやProjectとする
<!--ボタン-->
<button class="c-button"></button>
<!--ボタンの色違い+サイズ違いモディファイア-->
<button class="c-button c-button--blue c-button--small"></button>
Element
- オブジェクトの中の要素は
__
でつなげて.Block__Element
とする
__Elementの--Modifierは?
(参考:BEM記法におけるElement/Modifierの付け方メモ - Qiita)
どんなときにElementへのModifierを良いとするかなど細かくルールを決めないと破綻しやすく、第三者が見たときに分かりづらさもあった
オブジェクト全体の色が変わるなどは.p-card--green
などとして.p-card--green__header
のように。
オブジェクトの中の要素が右寄せやセンター寄せなどに変わるときなどは.p-card__label--center
や.p-card__label--right
のように指定した。
<div class="p-card p-card--green">
<div class="p-card__header p-card--green__header">
</div>
...
</div>
<div class="p-card p-card--green">
<div class="p-card__header p-card--green__header">
<div class="p-card__label p-card__label--center">
</div>
</div>
...
</div>
このように分けることでいろんなパターンのオブジェクトを作れる
JavaScriptから参照、操作されるクラス名
JavaScriptから参照されるクラス名
-
.js-button
のようにプレフィックスにjs-
をつけてJavaScriptで操作されることを明示する。 - JavaScriptでは
.js-button
に処理を与える
JavaScriptから操作されるクラス名
- プレフィックスに
is-
をつけ、定義する -
- JavaScriptでは
.is-click
が操作する処理を与える
- JavaScriptでは
-
is-click
自体にスタイルは定義せず、必ず、.c-button.is-click
にスタイルを定義する。(他のオブジェクトに対する.is-click
にスタイルが当たるのを防ぐ)
<button class="c-button js-button is-click"></button>
<div class="p-modal js-modalToggle is-click"></div>
同じ.is-click
というクラス名を使っても別々のスタイルを当てることができる。
カスケーディングに気をつける
カスケーディングとは,ある要素のあるプロパティに対する宣言が複数あるとき,宣言の“強さ”の関係を定めて,うち 1 つの宣言だけが有効になるようにするしくみである(抜粋:Let's begin CSS -- 2.5)
別のオブジェクト同士、別のオブジェクトを親とするセレクタを用いたカスケーディングは禁止
つまり下記のようにProject
を親に持つ別のProject
にスタイルを定義するのはだめ。
// Component
.c-button {
...
}
.c-dialog>.c-button {
/*c-dialogを親に持つc-buttonのみスタイルを指定してしまってる!ダメ!*/
...
}
// Project
.p-comments {
...
}
.p-articles>.p-comments {
/*p-articlesを親に持つp-commentsのみスタイルを指定してしまってる!そんな書き方ダメ!*/
...
}
理由として、
特定のモジュールに依存することなく、モジュールとして独立して再利用できるべきであり、混在させることによって他の開発者が予想しない挙動になるべきではないためです。
(抜粋:hiloki/flocss: CSS organization methodology.)
つまり、
- 汎用的であるべきパーツ(例:c-button)が他のComponent内でしか力を発揮できなくなるのはおかしい
- 混在することで第三者に、どれがもとの
c-button
のスタイルなのか、c-button
なのにここの下では変わってしまう、このc-button
を使いたいのにならない、といった混乱を招いてはいけない
ということ。
Project
がComponent
を変更することはOK
Component
の集まりをオブジェクトとしてProject
で管理してる場合、定義してる場合の余白などの調整は、Project
を親として持つComponent
にProject
の__Element
を並列して書くことで変更OKになる
<div class="p-profile c-media">
<img src="user.jpg" class="c-media__image">
<div class="c-media__body">...</div>
</div>
こういったときに、c-media__image
の余白をどうしても変えたい!となったら
// c-media.scss内
.c-media__image {
float: left;
margin-left: 10px;
}
//p-profile.scss内
.p-profile > .c-media__image { //←ここだめ!
float: right;
margin-left: 0;
margin-right: 10px;
}
のように変えるのは絶対にNG!
理由は、p-profile
内にc-media__image
があるだけで第三者的には混乱するし、p-profile
というObjectが成り立たないし、c-media__image
がp-profile
のなんなのか、理解しづらいため
そのため必ずクラス名をp-profile
のElement
ととし、c-media__image
と並列にかくこと。
<div class="p-profile c-media">
<img src="user.jpg" class="p-profile__avatar c-media__image">
<div class="c-media__body">...</div>
</div>
// c-media.scss内
.c-media__image {
float: left;
margin-left: 10px;
}
//p-profile.scss内
.p-profile > .p-profile__avatar { //←良い
float: right;
margin-left: 0;
margin-right: 10px;
}
Component
のモディファイア(パターン)を増やすことで対応可能な場合はそこで対応すること。
※ただしモディファイア(パターン)が2つ以上ある場合とする。
SASSのextend
例えば、.c-button
のModifierをつくるには、下記のように一つのタグへModifierのクラスもつける必要がある
<!--ボタン-->
<button class="c-button"></button>
<!--ボタンの色違いモディファイア-->
<button class="c-button c-button--blue"></button>
だが、SASSのextendで.c-button
を継承し、タグへの記載は.c-button--blue
クラスのみにすることが出来る。
.c-button {
...
}
.c-button--blue {
@extend .c-button;
background-color: blue;
}
これでc-button
クラスの記載の必要はなくなる!
クラスが増えることを防げる。
一見便利な@extendだが、BlockにModifierをつけて、.Block--Modifier__Element
としたとき、すべてのElementに@extend
しなくてはならなくてめんどくさかったので、廃止した
.p-box {
...
&__inner{
...
}
}
.p-box--round {
@extend .p-box;
...
&__inner{
...
@extend .p-box__inner;
}
}
.p-box {
...
&__inner{
...
}
}
.p-box--round {
...
&__inner{
...
}
}
まとめ
久しぶりにみたら、結構この記事見てもらえてるみたいで嬉しかったと同時にハチャメチャな文章を読み返して恥ずかしくなったので更新しました。