FLOCSSとは
「Foundation Layout Object CSS」
OOCSSやBEM、SMACSSのいいとこ取りをしたCSS設計思想。
構造
dartsassに移行しましたので構造を修正しました。
scss/
├─ foundation/
│ ├─ _base.scss
│ ├─ ...
│ ├─ index.scss
├─ global/
│ ├─ mixin/
│ │ ├─ _hover.scss
│ │ ├─ ...
│ │ ├─ index.scss
│ ├─ variable/
│ │ ├─ _color.scss
│ │ ├─ ...
│ │ ├─ index.scss
│ ├─ index.scss
├─ layout/
│ ├─ _header.scss
│ ├─ ...
│ ├─ index.scss
├─ object/
│ ├─ component/
│ │ ├─ _button.scss
│ │ ├─ ...
│ │ ├─ index.scss
│ ├─ project/
│ │ ├─ _header.scss
│ │ ├─ ...
│ │ ├─ index.scss
│ ├─ utility/
│ │ ├─ _color.scss
│ │ ├─ ...
│ │ ├─ index.scss
└─ common.scss
ディレクトリごとに読み込む
@import
が非推奨になりdartsassに移行したことで、読み込みには@use
を使用します。
後で説明する変数、mixinの global
ディレクトリの読み込みには@forward
を使用します。
各ディレクトリに設置したindex.scss
にディレクトリ内のSCSSファイルを読み込ませます。
そしてその index.scss
をcommon.scss
に読み込ませます。
読み込みをディレクトリごとにすることで、ファイル追加時に同じディレクトリのindex.scss
に記載すればいいだけなのでファイルが膨大になっても、追記しやすく時短になります。
// common.scss
@use "foundation";
@use "layout";
@use "object/component";
@use "object/project";
@use "object/utility";
// object/project/index.scss
// このファイルに追記するだけ
@use "header";
@use "drawer";
@use "nav";
...
Global
dartsassに移行したことで、変数、mixinは使用するファイル内に読み込ませる必要があります。
1行で読み込ませるためにglobal
ディレクトリを追加しました。
dartSassについてはこちらの記事を参考にしてください
Foundation
サイト全体のデフォルトスタイルを管理するディレクトリ。
reset.scss
ブラウザのデフォルトのスタイルをリセットするcssを定義。
destyle.cssをCDNで読み込んで使用しています。(アプデにすぐ対応できるから)
destyle.cssとは...
デフォルトのスタイリングをほぼ無くすことができるリセットCSS
- box-sizing: border-box;が含まれている
- iOSでタップした時のグレー表示も消してくれる
- marginは基本的に0
base.scss
サイトを構成する上で、デザインの基本の下地、土台となるスタイルを定義
- タグ自体にスタイルを定義
- reset.cssで足りなかったリセットを追加
- 基本の文字サイズやフォントファミリーなどの追加
Layout
各ページを構成する、ヘッダー、メインコンテンツエリア、コンテナ、フッターなどのレイアウトに関するスタイルをエリアごとに管理。
- 位置など、レイアウトの指定のみ定義
- 子要素は入れない
- 自身のデザインは Projectで定義する
l-wrapper.scss
body
のすぐ下で、ヘッダーとメインエリアとフッターをかこむ。
コンテンツが少ないときfooter
を下寄せに指定するのに必要です。
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
繰り返し使われる小さな単位のパーツを管理。
- ネストは2回まで
-
margin
など余白は定義しない。あくまでパーツとして扱う
例)
c-button.scss
c-label.scss
c-input.scss
…
Project
いくつかの Component と要素によって構成される、大きなブロックやエリアを管理。
-
Component
のように使いまわす。 - 使い回さないブロックでも分けて管理したい時。(セクションごとに分けるなど)
- 小さい単位の
Component
を集めて、一つのオブジェクトとして扱いたい時 - Componentとするには大きすぎる時
例)
p-card.scss
p-first-view.scss
p-button-group.scss
…
Utility
- プロパティ単体での指定を補助する。(
font-size
やmargin
padding
など) -
Component
、Project
を無駄に増やしてしまうことを防ぐ - あくまで補助としての役割。
-
sp only
やpc only
などレスポンシブの表示切り替え用クラスなど
u-margin.scss
u-padding.scss
u-color.scss
u-text.scss
...
命名規則
プレフィックスをつける
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。自分で言葉を作らないこと。(imgはタグ名にあるのでOK)
ただし、Utility
はクラス名が長くなりがちなので省略可とし、ファイル名は省略しないことで誰が見てもわかるようにする。
ケバブケース
-
linkbutton
のように繋げずlink-button
のようにケバブケースで書く - ページ固有のものは
about-card
のようにする
BEM命名規則MindBEMding
BEM命名規則とはBlock、Element、Modifierに分類して構成される規則。
Block
- オブジェクト、パーツ、エリアの名前。
Element
-
BLOCK
の中の要素たち -
__
でつなげて.Block__Element
とする -
.Block__Element__Element
はNG。 - ネストは3階層まで
// NG
<div class="p-about-section">
...
<ul class="p-about-section__list">
// .Block__Element__ElementはNG
<li class="p-about-section__list__item">
<a href="" class="p-about-section__list__item__link">
</li>
</ul>
...
</div>
// OK
<div class="p-about-section">
...
<ul class="p-about-section__list">
<li class="p-about-section__list-item">
// ネストはここまで
<a href="" class="p-about-section__list-link">
</li>
</ul>
...
</div>
Modifier
- オブジェクトの色違いやサイズ違いといった一部の見た目が違うとき
- 色名やサイズ名を
--
でつなぎ.Block--Modifier
のようなかたちにする。 -
.Block--Modifier--Modifier
はNG -
.Block__Element--Modifier
は__Element
自体の〇〇違いであればOK
<!--ボタン-->
<button class="c-button"></button>
<!--ボタンの色違い+サイズ違いモディファイア-->
<button class="c-button c-button--blue c-button--small"></button>
オブジェクト全体のModifier
のときは$this: &
を使う
- 子要素の
Element
にはModifier
はかかないでBlock
だけModifier
にする - SCSSの
$this: &
で子要素のElement
にスタイルをあてます
<div class="p-card p-card--green">
<div class="p-card__header">
</div>
<div class="p-card__body">
</div>
...
</div>
.p-card {
$this: &; // &(p-card)を$this変数にする
color: black;
&__header {
background-color: gray;
}
&__body {
background-color: white;
}
//Modifier
&--green{
color: green;
#{$this}__header { // .p-card--green .p-card__headerに変換
background-color: green;
}
#{$this}__body { //.p-card--green .p-card__bodyに変換
background-color: lightgreen;
}
}
}
Element
単体のみのModifier
もOK
- オブジェクトの中の一部の
Element
を右寄せにしたいときなどは.p-card__label--center
のようにElement
にModifier
を指定する - 2つ以上の
Element
が変わる場合は前項のオブジェクト全体のBlock
にあてる
<div class="p-card p-card--green">
<div class="p-card__header">
<div class="p-card__label p-card__label--center">
</div>
</div>
...
</div>
このように分けることでいろんなパターンのオブジェクトを作れる
ハイフン1つのクラス名は&-
で書かない
- 下記の例だと
__content
を探すときに一瞬混乱するので推奨しない
.p-card{
&__content{
}
&__head{
&-content{ //contentがかぶるのでこの書き方は非推奨
}
}
}
.p-card{
&__content{
}
&__head{
}
&__head-content{
}
}
JavaScriptから参照、操作されるクラス名
キャメルケースcamelCase
JavaScriptに関連するクラス名はキャメルケースcamelCase
でかく
JavaScriptから参照されるクラス名
-
.js-modalToggle
のようにプレフィックスにjs-
をつけてJavaScriptで操作されることを明示する。 -
.js-
のクラス名にはスタイルはあてない - JavaScriptでは
.js-modalToggle
に処理を与える
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
を使いたいのにならない、といった混乱を招いてはいけない
ということ。
<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>
Project
がComponent
を上書きできる
Component
の集まりをオブジェクトとして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つ以上ある場合とする。
Project
のなかのProject
Project
のなかの別のオブジェクトであるProject
は並列に書きません。
Project
は別のオブジェクトによる変更はできません。
※Project
のなかのcomponent
のように上書きはしない。
<div class="p-work-section">
<h2 class="p-work-section__title c-title"></h2>
<div class="p-work-section__card-list">
<div class="p-work-card">
...
</div>
</div>
</div>
ComponentとProjectの違い
人間で言えば、裸んぼの人間が最小単位のモジュールcomponentで、他にネクタイや靴下、スーツといったcomponentがあり、組み合わせてビジネスマンprojectになると考えられます
抜粋:FLOCSSを使ったCSS設計での悩みどころと解決案 - Qiita
- 小さい単位をComponent
- Componentの集まりをProject
- セクションなどの大きいオブジェクトも
Project
として管理します
アイコン付きボタンは.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>
@extend
は使用しない
例えば、.c-button
のModifierをつくるには、下記のように一つのタグへModifierのクラスもつける必要がある
<!--ボタン-->
<button class="c-button"></button>
<!--ボタンの色違いモディファイア-->
<button class="c-button c-button--blue"></button>
@extend
でc-button
を継承し、タグへの記載はc-button--blue
クラスのみ**にすることが出来る。
.c-button {
...
}
.c-button--blue {
@extend .c-button;
background-color: blue;
}
.c-button--small {
@extend .c-button;
background-color: red;
}
.c-button, .c-button--blue, .c-button--red {
width: 100px;
}
.c-button--blue {
background-color: blue;
}
.c-button--small {
width: 80px;
}
c-button--blue
のクラスのみで良いのでクラスの数が減るが、複数のmodifier
を指定するときに、結局c-button
のスタイルを二重指定してしまうことになるので@extend
は基本的に使用せず、c-button
自体も指定します。
<a class="c-button--blue" href="">
<a class="c-button--blue c-button--small" href="">
更新履歴
★2022/04/19 更新
約3年FLOCSSを使い倒し体の一部となった私のためのFLOCSS、編集し直しました。
★2022/08/19
自社のコーディングルール見直しついでに修正しました
参考記事
FLOCSS生みの親による基本ルール
ComponentとProjectの違い
命名規則
BEM
クラス名に迷ったら