1. はじめに
以前こんな記事書きいた者です(ありがたいことに数十に及ぶいいね(LGTM)頂いてます感謝)
Qiita執筆記事:CSS設計手法「FLOCSS」を適用して出てきた疑問を自分なりに考えて進めた結果と得た知見と失敗と
で、書いた上にある程度LGTM等頂いた責任として言わねばならぬことがあり本記事を記すこととした。まずは一言
「CSS設計素人がFLOCSSに手を出すのはおすすめできない」
(私が書いた記事でFLOCSS使ってみようと思った初心者がいたら本当に申し訳ない)
FLOCSSのObject層のルールが柔軟であるが故、使用者が方針を固める必要がある(と思っている)ので、方針を固めるための知見がない素人が使いこなすには難易度が高いと考えるからである。
※過去素人(私)が手を出した結果、上記記事のような悲惨な結果に陥った。別要因もあるが開発物は開発中断。
挫折を経て、CSS設計を一から勉強しなおさないと思っていた時に以下の本に出会い、PRECSSを知り、PRECSSの学習を通して前回の疑問点/失敗に対する解を得られたため、本記事ではそこを中心に記述する。
【Amazonリンク】本:CSS設計完全ガイド ~ 詳細解説+実践的モジュール集
2. PRECSSとは
本記事の本題の理解に必要な程度に紹介しておく。
※詳細は、上記の本やPRECSSのドキュメント(リンク:PRECSS公式サイト)等を読むべし。
PRECSSとは
- CSSを6つのグループ(+独自グループ追加可能)に分ける。
- グループには、各グループのクラスには2文字の接頭辞を付ける決まり。
- OOCSS、SMACSS、BEMの考えがベースとなり、作られている。
- FLOCSSに近しい部分があり、PRECSSとも対応が取れる部分がある
以下にPRECSSの各グループについての説明を記載する。
2.1. Base Group
- リセット CSS等のルールセット定義/標準スタイリングを行う層
- FLOCSS の Foundation層 とほぼ同等
2.2. Layout Group( 接頭辞: ly_ )
- ヘッダー/ボディエリア/メインエリア/サイドエリア/フッター等の枠を定義する層
- FLOCSS の Layout層 相当
2.3. Module Group
FLOCSS の Object 層相当のもの。
2.3.1. Block Module( 接頭辞: bl_ )
- 特有の子要素や、Element Module(説明は後述、Elementと略記)、他の Block Module を入れて、Webサイト内の複数箇所で使用できるようひとつの塊(ブロック)にまとめたもの
以下 Block Module(以降 Block と略記)のルールを記載
- Block にレイアウトに関わるスタイリング(float/width/margin 等)は基本行わない
- 他の要素に影響を及ぼさないスタイルのみ許容するが、Element としてスタイルを適用
- 例外:余白はモジュールに直接設定可。デザインがある程度統一されていればの話
- Block の幅/高さは初期値とする
- Element 同様 Modifier (.~__xxx) の適用可能 (詳細は次節)
<div class="bl_cardBox">
<div class="bl_card bl_cardBox_item"></div>
<div class="bl_card bl_cardBox_item"></div>
<div class="bl_card bl_cardBox_item"></div>
</div>
.bl_cardBox{
display:flex;
}
.bl_card {
box-shadow:
font-size:
line-height:
}
// レイアウトは以下で指定
.bl_cardBox_item{
width:
margin:
}
// 以下でもOK
.bl_cardBox > .bl_item {
width:
margin:
}
2.3.2. Element Module( 接頭辞: el_ )
- ボタン、ラベル、見出し等の最小単位のモジュール
- どこにでも埋め込み可
- Modifier (.~__xxx) による拡張パターン実装(上書き)可能
- あしらい(Detail/装飾)を変えるとき
- 大きさを変えるとき
- 一定の規則にしたがって振舞いを変える(カラムなど)
Block / Element 区別の指針の参考
- 他の様々なモジュールに埋め込まれることが多い = Element
- ルート要素と子要素を含めおおよそ要素数が3つ以内 = Elememnt
- 上記以外? = Block
<span class="el_label">
<button class="el_btn el_btn__small"></button>
</span>
.el_btn {
width: 300px;
}
.el_btn.el_btn__small { // Modifierを使用して上書き
width: 200px;
}
2.4. Helper Group( 接頭辞: hp_ )
- ここだけスタイルを調整したい時に使用
- 命名は modifier と同じく「keyValue」
.hp_mb20 {
margin-bottom: 20px; !important;
}
.hp_mt2e {
margin-top: 2em; !important;
}
.hp_mt2_5e {
margin-top: 2.5em; !important;
}
.hp_MT2e { // ネイティブな値はkeyを大文字に
margin-top: -2em; !important;
}
2.5. Unique Group( 接頭辞: un_ )
- ある 1 ページ(もしくは特定の1箇所)でしか使わないことを明示するグループ
- 他とは分離して管理するイメージ
- ドキュメントの扉ページのような特別なページ
- 通常ページでもモジュール設計から外れた場所(
position:absolute;
頻発箇所)に使用
2.6. Program Group(接頭辞: js_ is_ )
- js_:JS で要素を取得するためのクラス
- is_: 要素の状態を管理するためのクラス。!important 使用推奨
- bl や js と組み合わせて使う(単体だと他の箇所に影響を与える可能性があるので)
.js_accordion_body .is_active {
display: block;
}
2.7. Original Group (独自のグループ)
例えば
- オリジナルのグリッドレイアウト: .gr_
- column: .cl_
- デスクトップ幅のみ有効: .lg_
- タブレット幅以下のみ有効: .md_
- スマホ幅のみ有効: .sm_
3. 本題:FLOCSSでの失敗 → PRECSSを通しての学び
PRECSSで得た学びにより、FLOCSSの前記事5章の節タイトルにもなっている、色々な疑問点を解消できた上、新たなる気付き事項もあったため、前記事の各節に紐づけて(※)記載する。
(※)前記事の節番号+タイトル一部を、以降の節タイトルに含めることで紐づける。以降の節タイトルが長いのはご容赦を
前記事リンク:CSS設計手法「FLOCSS」を適用して出てきた疑問を自分なりに考えて進めた結果と得た知見と失敗と
3.1.「5.1. Layout層とObject層(Module)の境界迷子」打破のための考え方
FLOCSSに手を出していた時は、境界がよく分からなくなっていた。
特に「Layout層とObject層(Module)が重なったり、Objectの中にLayoutが入り込むケースがある」と考えたのが一番の混乱の元だった。境界を明確すべく、以下のように考えた方が良かった。
-
Layoutは、枠組みであり
-
Moduleは、枠組みに当て込むだけのブロックであり
-
LayoutとModuleの両者が重なる部分があってはならない
ここで言う「重なる」というのは、以下のような状態のこと
<div class="ly_main">
<div class="ly_main_block bl_block"> <!-- 重なってる部分 -->
<div class="bl_block_element1">テ</div>
<div class="bl_block_element2">ス</div>
<div class="bl_block_element3">ト</div>
</div>
</div>
何故か
以下図のような枠組み(Layout)と当て込んだ Block があったと仮定して、同じ Block を他の枠に使いたい場合を考えてみる。
CSSのクラスを上部のHTML例のような構成にしていると、
bl_block
のレイアウトは、少なからず ly_main_block
に依存していることになる(=bl_block
のレイアウトは ly_main_block
(作り方によっては ly_main
も) が前提となり、無いとレイアウト崩れに繋がりかねない)。
そのため、
- 他の枠で使おうとすると(
bl_block
は)そのままでは使えない class になりかねない(何かしら手を入れないと使えないとか) - (
bl_block
を置きたい枠にly_main_block
相当の)class が新たに必要 or 既にあるなら class に余計に手を加える必要が出てきかねない
CSS 設計で実現したい class を管理しやすく使回しが利くものにすることから外れてしまう。
※上部のHTML構成でも、ちゃんと境界を意識して作れば回避はできるが、混乱の元になるので完全に分けることを勧める。
以下のように、依存を断ち切り独立させることで、最初から Block 部分のHTMLをコピペするだけで他のところでも使えるようにすることを意識した方が良い。
<div class="ly_main">
<div class="bl_block">
<div class="bl_block_element1">テ</div>
<div class="bl_block_element2">ス</div>
<div class="bl_block_element3">ト</div>
</div>
</div>
場合によっては、以下のようになることもあるかなと
<div class="ly_main">
<div class="ly_main_block">
<div class="bl_block">
<div class="bl_block_element1">テ</div>
<div class="bl_block_element2">ス</div>
<div class="bl_block_element3">ト</div>
</div>
</div>
</div>
だが、大抵は枠に Block を当て込むだけで終わりはしない。
当て込む際に、枠に合うように余白や見てくれ等を調整が必要になってくる。
Block内のスタイル(色やフォント等)を一部変えたいとかであれば、Modifier での上書きで済むが、Layout と Block の境目の調整(余白やBlockのサイズ感 etc.)したい際、Layout と Block に下手に手を入れると使い回しの利かない class になる。
どうすれば Layout と Block のclassに手を入れず境目の調整を実現できるか。その鍵は、Utility (Helper)にある。
そこについては、次節に記載する。
【補足事項】
前記事でも触れた display:flex
については、同じ設定で複数箇所で使うことがある。しかし、使用用途にバラつきがある(枠を定義するためだったり要素の並びを定義するためだったり etc.)。
なので私は、display:flex
のみ(デフォルト設定)で済む箇所以外は、flex用の独自グループ追加して実装をひとまとめにし、LayoutやBlock等のクラスで必要な所に継承して管理している(以下SCSSイメージ)
.bl_btnUnit {
height: 100%;
@extend .fl_columnCenter;
// 略
}
// flex 縦並び中央配置
.fl_columnCenter {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
}
3.2.「5.4. Project層(Block)とUtility層(Helper)の境界に迷った」結果「5.5. 最終的にUtility層(Helper)はスカスカに...」なる訳がない、Helperの存在意義を理解せよ
FLOCSSに手を出していた時は、Utility層(Helper)の存在意義が分かっておらず陥った失敗だが、理解したので説明する。
例えば、
-
要素間の余白や細かいサイズ感の調整のために、
margin
やpadding
で細かい調整をするだろう。
ある Block を他の枠にも使う際は、その枠に合った調整が必要になるだろう。 -
だが複数箇所での使用を前提とする Block の class に1箇所でしか使い物にならない調整を入れる訳にはいかない。
-
まずは Blockを複数箇所で使用できるよう、特定の枠に入れるための調整を入れていない汎用的な Block に留めておく。
-
そして、実際にその汎用的な Block を枠に入れる際に、枠に適した状態にするための細かな調整を担うのが Helper の存在意義である(と解釈している)。
そのため、以下含む要素間の調整にかかる部分はHelperに任せた方が、Blockを使い回しの利くコンポーネントたらしめることができると考える。
- LayoutとBlockの境界の調整
<div class="ly_one">
<div class="bl_block hl_mrl5">
<div class="bl_block_element1">テ</div>
<div class="bl_block_element2">ス</div>
<div class="bl_block_element3">ト</div>
</div>
</div>
- とある Layout 内の Block 間の調整
<div class="ly_two">
<div class="bl_todolist hl_mtb5">
<div class="bl_todo"></div>
<div class="bl_todo"></div>
</div>
<div class="bl_todolist hl_mtb5">
<div class="bl_todo"></div>
<div class="bl_todo"></div>
</div>
</div>
- Block内の要素間の調整
<div class="bl_todolist">
<div class="bl_todo hl_mtb5">
</div>
<div class="bl_todo hl_mtb5">
</div>
</div>
【補足事項】
要素の重ね順の指定に使う z-index
も HTML 側で俯瞰して見えた方が個人的には管理しやすかったので、Helperクラスで管理している。
3.3.「5.3. Project層(Block)とComponent層(Element)の分類が曖昧」にならずに済むのが PRECSS の良い所
初心者だがどうしても FLOCSS やりたいという方は、PRECSS の Block / Element の区分指針を参考に Project / Component 層に当てはめるのが良いと考える。
Element の基準として以下を意識し Element を決定し、複数の Element を Block としてまとめ上げ、必要によっては、その Block を上位の Block に含ませ、組み上げていくイメージを持てばやりやすいかと考える。
- 「ボタン、ラベル、見出し等の最小単位のモジュール」であること。
- いくつかの最小モジュールを組み合わせまとまりとすることを許容し「ルート要素と子要素を含めおおよそ要素数が3つ以内」とすること。
個人的な Elment / Block の粒度のイメージとしては、以下の通り(具体的な例は下部図参照)。
- Elemntは、Atomic Design で言うところの「原子」以上 ~「分子」未満
- Block は「分子」以上 ~ 「生体」以下
Atomic Design の参考リンク:Atomic Design を分かったつもりになる
3.4. 上記以外4項目への追記事項
特に記しておきたかったのは上記までなので、歳月が経った今だから言えることを個人的考えを多分に含めて簡単に綴っておく(興味があれば一読頂ければと思う)。
3.4.1. 「5.2. 後から気付いたLayout層はGridLayout使えば良かった説」に対して
GridLayout に拘る必要はなく、組みたい枠組みを組むために最適な方法でやればよい。凝った枠組みでなければ、GridLayout がやりやすいだろうねという話。凝ったデザインでなく GridLayout で枠組みができるなら CSSフレームワークのGridLayoutシステムにのっかった方が良いか一考の余地があると思う。
3.4.2. 「5.6. ファイルの分け方を誤った件」に対して
CSSのファイル分けも、プログラミングの良い書き方(1クラス/1メソッドの役割は1つ、1ファイル100行まで etc.)同様に、どこに何があるかが、部品(コンポーネント)ベースでフォルダ/ファイル名から識別できるように管理するべきだったと思う。
そうしておかないと使い回しする際に迷子になる。
多少ファイルを細かく分けてしまったとしても SCSS で実装していれば、1つのCSSファイルにまとめられるので、あまり問題にはならないはず。
3.4.3.「5.7. うまく行かなかった要因」に対して
やり方の問題と言うより、単純にCSS設計を行うための知識が足りなかっただけな気がしている。
Atomic Design や コンポーネント指向設計 等の知識を入れて コンポーネントベースに組み上げて行っていれば、(失敗という結果は変わらなかっただろうが)もう少しマシな状態になったのではないかと思う。
3.4.4. 「5.8. 設計手法に曖昧な部分には独自ルールを考えてもいい説」に対して
FLOCSSであろうが、PRECSSであろうが、他の設計手法であろうが、その型に縛られず、作る対象に向かい合い、適切な設計方針を定め、作る必要があると考える。
(それができる境地に到達するのが難しいので)まずは、自分なりのやりやすいやり方を模索するのも良い一手かもしれない。
一参考として、以下記事を引用する。
【FLOCSS・BEM】私なりのCSS・HTML設計シート【PRECSS・吉本式BEM】
【補足事項】
個人としては、作りたいものがWebサイトよりはWebアプリで、PRECSSはWebアプリも考慮したグループ分けがされているため、PRECSSをベースにしつつ、グループ分けした方が良いと思ったものを独自グループにしていく方法で十分やりやすいと感じている。また、CSSフレームワーク、JSフレームワークと組み合わせた際に、どういった独自グループを追加するのが良いかを試す必要があると考えている。
4. 蛇足:お主は何の根拠(実体験)を元に上記の話をしとるんかい?
本記事の内容は、実際にPRECSSを試した体験ベースの話であり、作ったのものがある訳で...
(2020/8末現在) 完成していない(上、一部レイアウトのバグ/変更必要箇所に手が回っていない)が、現状の見た目は以下の通り。
前回は、でか物を作ろうとしたがために失敗に終わったこともあり、見た目は割とコンパクトなものにしている。
(2020/8末時点で開発中ではあるが) 今回は参考までにリポジトリのリンクを記載しておく。
リポジトリ: https://github.com/Symthy/TodoList-ts-pre
参考になるかは分からないが、README にも記載している通り、うろ覚えでFLOCSSっぽく実装して失敗したもの(修正前)と PRECSS に書き換え後(修正後)のブランチを残しているので、気になる方は、両ブランチの差分を見てみるといいかもしれない。
-
修正前: https://github.com/Symthy/TodoList-ts-pre/tree/record/before-refactor-flocss-modoki
-
修正後: https://github.com/Symthy/TodoList-ts-pre/tree/record/after-refactor-flocss-modoki-to-precss
5. おわりに
PRECSSを使ってみて、その使いやすさを実感した。本記事には、PRECSS の断片的な情報しか記載していないため、詳細や実践的Tipsを知りたい方は以下の本を読むことを勧める。
【Amazonリンク】本:CSS設計完全ガイド ~ 詳細解説+実践的モジュール集
PRECSSを使いやすいと一番実感した瞬間は、(機能の実装のため、HTML/CSS全く触らずTypeScriptゴリゴリ書き進めていたがために気付いたら)1ヶ月強程HTML/CSS放置し頭から吹っ飛んで頃に行った HTML/CSS 追加修正がしやすかった時である。
グループ分けしっかりされていた方が、理解/修正しやすく開発の効率化に繋がると感じた。
4章記載の開発物は、TypeScript 及び 拡張性/理解容易性を重視したコーディングとして練習物でもあるので、そのうちその方面の記事を書ければと思う。
以上