432
Help us understand the problem. What are the problem?

posted at

updated at

Organization

[CSS設計] 私のためのFLOCSSまとめ

★2022/04/19 更新
約3年FLOCSSを使い倒し体の一部となった私のためのFLOCSS、編集し直しました。

FLOCSSとは

「Foundation Layout Object CSS」
OOCSSやBEM、SMACSSのいいとこ取りをしたCSS設計思想。

構造

:file_folder:Foundation サイト全体のデフォルトスタイルを管理。
┣ reset.scss :exclamation: CDNで読み込むようにしたよ
┣ base.scss
┣ variable.scss サイト全体で使える変数
┣ mixin サイト全体で使えるmixinを管理
:file_folder:Layout 各ページを構成するサイト全体で共通したエリアを管理。
┣ l-wrapper.scss
┣ l-header.scss
┣ l-main.scss
┣ l-footer.scss
:file_folder:Object サイト全体で再利用できるパターンを持つオブジェクトを管理
:file_folder:Component 小さな単位のオブジェクトを管理(ボタンなど)
 ┣ c-button.scss
 ┣ c-grid.scss
 ┣ …
:file_folder:Project いくつかのComponentと、他の要素によって構成される大きな単位のオブジェクトを管理
 ┣ p-card.scss
 ┣ p-profile.scss
 ┣ ...
:file_folder:Utility ComponentとProjectのモディファイア(パターン)で解決することができないスタイル、また、調整のための便利クラスなどを管理。
 ┣ u-utility.scss
 ┣ u-color.scss
 ┣ u-margin.scss
 ┣ u-padding.scss
:file_folder:Theme テーマによる色の切り替えなど、ページ単位の色違いとか
 ┣ t-blue.scss
 ┣ ...

:open_file_folder: Foundation

サイト全体のデフォルトスタイルを管理するディレクトリ。

:page_facing_up: reset.scss

ブラウザのデフォルトのスタイルをリセットするcssを定義。
私はdestyle.cssをCDNで読み込んで使用しています。(アプデにすぐ対応できるから)

デフォルトのスタイリングをほぼ無くすことができるリセットCSS

  • box-sizing: border-box;が含まれている
  • iOSでタップした時のグレー表示も消してくれる
  • marginは基本的に0

(抜粋:2020年版!おすすめのリセットCSSまとめ#destylecss | Web Design Trends

:page_facing_up: base.scss

サイトを構成する上で、デザインの基本の下地、土台となるスタイルを定義

  • 基本的にタグ自体にスタイルを定義すること。
  • :page_facing_up: reset.cssで足りなかったリセットを追加。
  • 基本の文字サイズやフォントファミリーなどの追加。

:open_file_folder: Layout

各ページを構成する、ヘッダー、メインコンテンツエリア、コンテナ、フッターなどのレイアウトに関するスタイルをエリアごとに管理。

  • 位置などレイアウトの指定のみ定義する
  • 子要素はなるべく入れない
  • 中身のデザインは :open_file_folder: Projectで定義する
l-header,p-headerの例
//ページ全体からみたレイアウトに対するスタイル
.l-header {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 100;
  width: 100%;
}
//ヘッダーというパーツのスタイル
.p-header {
  display: flex;
  &__logo{
    width:100px;
    height:50px;
    ...
  }
  ...
}

:page_facing_up: l-wrapper.scss

bodyタグのすぐ下で、ヘッダーとメインエリアとフッターをかこむ

:page_facing_up: l-header.scss

ヘッダーの位置のスタイルを定義

:page_facing_up: l-nav.scss)

navは画面構造によって.p-navとして.l-headerのなかに入ることもあるが、.l-という大枠のほうがやりやすければLayoutでOKとする。

:page_facing_up: l-main.scss

メインのコンテンツエリアの共通スタイルを定義

  • ヘッダーのfixed分の相殺や余白など
  • 共通の上下左右の余白などのみの定義

:page_facing_up: l-container.scss

サイトのメインエリア内のコンテンツ幅などの指定

:page_facing_up: l-footer.scss

フッター位置のスタイルを定義

:open_file_folder: Object

繰り返し色んな場所で使うパーツやブロックをすべてObjectと定義。

  • 小さな単位のパーツを :open_file_folder: Component に。
  • Componentが集まったブロックや、大きなエリアは :open_file_folder: Project に。

:open_file_folder: Component

色んな場所で使われてる小さな単位のパーツを管理。

  • ネストの回数を決めておくと破綻しない
  • marginなど余白は定義しない。あくまでパーツとして扱う

例)
:page_facing_up: c-button.scss
:page_facing_up: c-label.scss
:page_facing_up: c-input.scss

:open_file_folder: Project

いくつかの :open_file_folder: Component と要素によって構成される、大きなブロックやエリアを管理。

  • 使い回さないブロックでも分けて管理したいときにも。
  • 小さい単位のComponentを集めて、一つのオブジェクトとして扱いたい時
  • Componentとするには大きすぎる時

例)
:page_facing_up: p-card.scss
:page_facing_up: p-first-view.scss
:page_facing_up: p-button-group.scss

p-cardの例
<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は、:open_file_folder: Component として管理する。

:open_file_folder: layout で管理しない理由は各ページで必ず使うとは限らないから
:open_file_folder: Utility で管理しない理由は :open_file_folder: Project と組み合わせて使用する場面もあるため。

c-gridの例(.pと組み合わせる)
<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>

:open_file_folder: Utility

  • Component、Projectのオブジェクトを無駄に増やしてしまうことを防ぐ。
  • Projectとして定義するには要素が少なすぎる場合やCmponent単体で使うときに使用
    オブジェクト自体が持つべきではないmarginの代わりに.u-mb10 { margin-bottom: 10px; }のような隣接する要素との間隔をつくるといった役割。
  • すべての余白marginをUtilityクラスでつけることはしない。
  • あくまで補助としての役割であること。

:page_facing_up: u-margin.scss
:page_facing_up: u-padding.scss
:page_facing_up: u-color.scss
:page_facing_up: u-text.scss

...

↓↓↓↓↓↓↓ あなたの記事の内容

命名規則

───────
Component、ProjectのObjectを無駄に増やしてしまうことを防ぐ。
Object自体が持つべきではないmarginの代わりに.u-mb10 { margin-bottom: 10px; }のような隣接するモジュールとの間隔をつくるといった役割。
↑↑↑↑↑↑↑ 編集リクエストの内容

プレフィックスをつける

:open_file_folder: layout なら.l-:open_file_folder: Componentなら.c-のように、属してるフォルダ名の{頭文字}+- でプレフィックスを頭につけ、ファイル名だけでもで判断しやすくする(Foundationはリセットや下地が目的なので除く)

:page_facing_up: l-header.scss
:page_facing_up: l-footer.scss
:page_facing_up: c-button.scss
:page_facing_up: p-card.scss

ファイル名 = クラス名

l-header.scssには.l-headerというクラス名を定義

ファイル名、クラス名は省略しない

  • 誰が見てもわかるようにファイル名、クラス名なるべくは省略しない
    txtbtnも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とする

例) .Block--Modifier
<!--ボタン-->
<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のように指定した。

オブジェクト全体に同じModifier
<div class="p-card p-card--green">
  <div class="p-card__header p-card--green__header">
  </div>
  ...
</div>
オブジェクトのなかの要素だけModifier
<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が操作する処理を与える
  • is-click自体にスタイルは定義せず、必ず、.c-button.is-clickにスタイルを定義する。(他のオブジェクトに対する.is-clickにスタイルが当たるのを防ぐ)
JavaScript用のクラス
<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を使いたいのにならない、といった混乱を招いてはいけない

ということ。

ProjectComponentを変更することはOK

Componentの集まりをオブジェクトとしてProjectで管理してる場合、定義してる場合の余白などの調整は、Projectを親として持つComponentProject__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__imagep-profileのなんなのか、理解しづらいため

そのため必ずクラス名をp-profileElementととし、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{
    ...
  }
}

:rolling_eyes:

まとめ

久しぶりにみたら、結構この記事見てもらえてるみたいで嬉しかったと同時にハチャメチャな文章を読み返して恥ずかしくなったので更新しました。

参考記事

FLOCSS生みの親による基本ルール

ComponentとProjectの違い

命名規則

BEM

クラス名に迷ったら

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
432
Help us understand the problem. What are the problem?