個人で制作する場合、BEM、とりわけ、MindBEMdingモデルをCSS設計として採用している。
実際の運用では、Blockごとにscssファイルを新規追加もしくは修正して保存すると、自動でファイル結合し、minified化(1行にする)するようにしている。このサイトもそうしている。
https://sample27.simplesimples.com/wp-content/themes/smplsmpls/assets/css/simplesimples.min.css
このファイルは、50以上のscssファイルで構成されているが、新規追加時も修正時も迷うことは一切ない(どれだけ増やしても問題ない)。これはBEMの良いところで、class名で悩むことも、すでに使われたclass名か心配する必要がなく、ディレクトリ構成をシンプルに保てるため実現できている。作業効率が劇的に上がることは言うまでもない。
新規やフルリニューアル案件で、自分が主導させていただく場合は、間違いなくこれを導入する。
だがしかし、そんな案件ばかりではない。
最近、今まで見てきた中で圧倒的にワーストな状態のCSS運用にチャレンジさせていただくことになった。
CSS設計はない、Bootstrapを使用している、一部でSassを導入しているが、コンパイル後のCSSを修正しているファイルもある、importantを乱立させている(importantの上書きをimportantでしている)、一ファイルに大量のCSS(1万行以上)が記述されている。それが複数ファイルあり、さらには重複されたスタイルも散乱している。一つのクラスを修正しようと思ったら、該当箇所がいくつもある。外部化されているわけでなく、html側にstyleタグやstyle属性で指定されたスタイルもかなりある。breakpointの使い方が整数の場合しかケアされていない。すでに複数人がそれぞれのルールで記述している。
なぜ、CSS設計?
もちろん、このような状況は何も見なかったことにし、改修用のCSSファイルを用意し、それを全てのページで読み込んでいるCSS内に@importで呼び出して修正することは、最もイージーですぐに対応可能だ。
だがしかし、それでいいのか?
担当者はCSSスキルが初心者から上級者まで複数人いる状況で、これから数年先の運用を視野に入れた際、その間に担当者が変わっても同様のコードを書き続けられ、さらにCSSの新しい仕様が適用されたときも適用できることを考慮したい。
そのためには、CSSコーディングガイドを用意することで解決できる。その指針としてCSS設計の導入が便利なのだ。すべてオリジナルでルール作りすることもできるが、今時ではない。世はコラボ時代、一般的に浸透しつつあるルールを適用することで、導入コストをグッと抑えてメンテナブルにしたい。
前置きがだいぶ長くなってしまった。CSS設計といえば、OOCSS、SMACSS、BEM、FLOCSS、MCSS、SUIT CSS、MVCSS、ITCSS などいろいろあるが、ここでは、OOCSS、SMACSS、BEM、FLOCSS についてまとめてみたい。
4つのCSS設計を知る
プロジェクトごとに最適なCSS設計は異なってくる。また、選択したCSS設計を厳格に守らなければいけない、ということもない。現実には、選択したCSS設計をベースとして、プロジェクト固有のルールを追加していくことになるだろう。
CSS設計の構成
CSS設計はどれも似たような構成をしている。その上でそれぞれに独自ルールがある。基本的にSMACSSを理解してしまえば、他の理解は早い。ただし、SMACSSの固有パーツをレイアウトとして扱うか、再利用可能として扱うかは、クラス名ルールに影響するので、事前に決めておくべきポイントとなる。
OOCSS | SMACSS | BEM | FLOCSS | |
基本設定 | base | foundation | ||
レイアウト | コンテイナー | layout | Block | layout |
固有パーツ | コンテンツ | layout or module | Block | project |
再利用可能 | オブジェクト | module | Block | component |
構造 | ストラクチャー | base module | element | component |
見た目調整 | スキン | sub module | modifier | utility |
状態変化 | state | |||
テーマ別 | theme |
OOCSS
Object Oriented CSS の略。オブジェクト指向CSSと和訳されていることが多い。
重要なポイントは2つ。構造と見た目を分離することと、コンテナーとコンテンツを分離する、ということ。もう少し、噛み砕いてみる。
- オブジェクトは、構造と見た目で構成する
- 1つ以上のオブジェクトは、コンテンツ内に配置する
- コンテナーは、コンテンツを囲むエリアである(何を囲むかは問題ではない)
オブジェクトを意識した記述となり、自由度が高いのが特徴。
CSSの基本的な考え方、という印象。これだけではさまざまな解釈が可能となるので、ある程度の運用ルールを用意しないと破綻しそう。
SMACSS
CSSを5つにカテゴライズする
実際のディレクトリ構成も合わせておいた方がわかりやすい
base | 基本となるスタイル。ノーマライズや基本となるフォントサイズやリンクカラーなど。idやclass名は使わず、あくまで要素に対して指定する。!importantは使わない。 |
layout | ヘッダー、フッター、カラムといった、Webページのレイアウトを決定する要素 (l-, layout-, grid- などプレフィックスをつけ、クラス名で指定することを推奨) |
module | Webページを構成するパーツ。ボタン、タブ、リストなど再利用可能できる要素(プレフィックスはなしで、クラス名で指定することを推奨) base module(構造) と sub module(見た目) を分けて複数クラスとして使うこともある |
state | JSの操作によって付与されたクラス名されたことでスタイルが変化する要素(is- のプレフィックスをつけ、クラス名で指定することを推奨。上書きのための !importantを許可する) |
theme | テーマに応じてWebページ全体、または一部の見た目を変えるスタイル |
パーツごとにscssファイルを作れるかがポイントとなりそう。そこの運用がしっかりできないと破綻しそう。
themeは使うことはほぼないかな。
BEM
CSSを3つにカテゴライズする
カテゴライズしているだけで、実際のディレクトリ構成とは関係ない
Block | Webページを構成するパーツ。ヘッダー、フッター、カラム、ボタン、タブ、リストなど |
Element | Blockを構成する要素。必ずBlockに内包される |
Modifier | BlockやElementの見た目を変える要素 |
クラス名を見れば、それだけで影響範囲を把握できる。BEMの独特なクラス名の付け方と長いクラス名は支持されにくい。
FLOCSS
CSSを3つ、さらにその一つには3つにカテゴライズする
さらに記述順を明確化していて、foundation が一番弱く(最初に記述)、utilityが一番強い(最後に記述)状態にする
実際のディレクトリ構成も合わせておいた方がわかりやすい
foundation | 基本となるスタイル。ノーマライズや基本となるフォントサイズやリンクカラーなど。 | |
layout | ヘッダー、フッター、カラムといった、Webページのレイアウトを決定する要素 | |
object | component | Webページを構成するパーツ。 |
project | componentより大きい固有のパーツ。記事一覧やユーザープロフィール、画像ギャラリーなど | |
utility | わずかなスタイル調整のために使うクラスやclearfixのような汎用的に使うクラス |
実践
CSS設計をベースにプロジェクトごとに最適化したルール作りが必要となるわけだが、ここからはもうちょっと細かく構成や記述を考えてみたい。
それぞれのCSS設計を踏まえて、実際にSassを用いた運用をイメージする。
すべてのCSS設計に足りないのだが、Sassを用いるということは変数やmixin、extend、keyframesのような関数を別に用意して最初に読み込むようにしたいので、そのためのディレクトリを、settingとして用意する。
setting | variable | 全体で使う変数(色やフォントなど) |
functions | mixinやextend、keyframesのような関数 | |
import.scss | importファイル一覧 |
記事の最初の比較表を見直してみる。
OOCSS | SMACSS | BEM | FLOCSS | |
1. Sass用 | setting | setting | setting | setting |
2. 基本設定 | base | foundation | ||
3. レイアウト | (コンテイナー) | layout | (Block) | layout |
4. 固有パーツ | (コンテンツ) | layout or module | (Block) | project |
5. 再利用可能 | (オブジェクト) | module | (Block) | component |
6. 構造 | (ストラクチャー) | (base module) | (element) | component |
7. 見た目調整 | (スキン) | (sub module) | (modifier) | utility |
8. 状態変化 | state | |||
9. テーマ別 | theme |
ディレクトリ構成案
ディレクトリ構成があらかじめ決められているのは、SMACSSとFLOCSS。OOCSSとBEMはとくに決まりはない。さらに、FLOCSSでは記述順が明確化されているが、これはSMACSSも同様と考えたい。
import.scss は便宜上入れておくが、個人的にはファイルを追加するたびに、import.scss に追加する手間を省いている。そのため、import.scssの運用はない。これは作業効率化においては欠かせない。
OOCSS | SMACSS | BEM | FLOCSS |
┗ setting ┗ variable ┗ functions ┗ base ┗ layout ┗ object ┗ import.scss | ┗ setting ┗ variable ┗ functions ┗ base ┗ layout ┗ module ┗ state ┗ theme ┗ import.scss | ┗ setting ┗ variable ┗ functions ┗ base ┗ bem ┗ import.scss | ┗ setting ┗ variable ┗ functions ┗ foundation ┗ layout ┗ object ┗ component ┗ project ┗ utility ┗ import.scss |
OOCSS
layoutの中に、コンテイナーとコンテンツを入れる。
ファイル名からコンテイナーかコンテンツか判別できるようにする
BEM
ディレクトリ構成を見るだけでも、BEMはクラス名の書き方が独特なだけで、最もシンプルなCSS設計と言える。再利用可能とするエリアが他のCSS設計に比べて、大きな括りとなっているため注意が必要だが、これもまた最も牢固なCSS設計と言える(完全な独断と偏見)。
SMACSS/FLOCSS
とくに補足はない。
注意)JavaScriptによって見た目を変更する場合
一つだけ先にルールを決めておきたいのが、JavaScriptによって見た目を変更した場合をどのように扱うか、ということだ。SMACSSでは、stateがあるが、それ以外のCSS設計を採用する場合は明確にしておきたい。個人的には、SMACSSの is- をプリフィックスとしてつけるルールは賛成だが、そのため別フォルダ・別ファイルで管理したくない。
また、JavaScriptのフックとして利用する記述ルールも決めておきたい。
- クラス名を使用し、js- というプレフィックスをつける
- CSSによるスタイルは行わない
この2つを徹底することで、JSとCSSの確実な分離が可能となり、CSSのクラス名を変えたらJSが動かなくなるというリスクを回避できる。
記述ルール
そもそものCSSとしての記述ルールは、Google HTML/CSS Style Guide を大前提するのが一番だと思っている。どこぞの誰かが作ったスタイルガイドより説得力があり、受け入れやすい。これを基準として、JavaScriptのフックのルールなど、不足分や差異がある場合は明記するという流れだ。
SCSSでは、色々な書き方ができる。
そのため、BEM以外のCSS設計では、例えばバリエーション違いの記述ルールであれば、下記のようなルールがあった方がいい。
NG .primaryというクラス名の存在に配慮してください
.button {
&.primary {
}
}
NG 複数クラスで指定してください
.button {
&-primary {
}
}
NG 並び順が変わって、スタイルの適応順が変わらないようにしてください
.button {
}
.button-primary {
}
OK
.button {
&.button-primary {
}
}
複数クラスで指定するのは賛否あると思う。個人的には、htmlからクラス名で逆引きする際、複数クラスで指定していた方がすぐに見つかるので、賛成派だ。
この例では、バリエーション違いの例に、primaryを使ったが、実際には大きく3つの分け方がある。
役割や機能 | .button-primary, .button-active |
見た目 | .button-blue, .button-circle |
記号や番号 | .button-a, .button-b, .button-1 |
BEMの場合、CSSの書き方が決まっている。
そのため、html側はmodifierを使う場合は複数クラス指定することとなるが、SCSS側は複数クラス指定の縛りから解放される。
- Block
- Block__Element
- Block_Key_Value
- Block__Element_Key_Value
上記の例では、.button が blockの場合、下記のように記述することになる。
.button_type_primary
ただ、個人的にはmodifier を Key_Value で記述するのに抵抗がある。そこをケアした書き方をMindBEMdingモデルから採用したい。
- Block–Modifier
- Block__Element–Modifier
つまり、BEMではModifierの区切りは_(アンダーバーひとつ)だが、MindBEMdingモデルでは–(ハイフン2つ)となり、Key_Valueのような従属関係の記述もない。これであれば、ハイフンが2つ並んで書かれていれば、バリエーション違いとすぐに分かるわけだ。
上記の例では、.button が blockの場合、下記のように記述することになる。
.button–primary
MindBEMdingモデルの紹介ついでになるが、もう一つの特徴として、すべての命名をBEMに縛らない、というのもポイント。実際に運用を進めると、とくにJavaScriptのフックで追加するクラス名はBEMルールではなく、独自ルール(たとえば、is- とプリフィックスを追加し、複数クラスで指定するというルール)にするだけで、すこぶるスッキリする。
この辺りは、あらかじめ想定できる記述ルールを記載しつつも、ガチガチに決めるというより現場で運用しながら追加していく、ということで良いと思う。
さいごに
久しぶりに休日一日使って、「CSS設計」で検索した記事をいろいろ参考にさせていただきながら、後半は個人的な見解を多く含ませながら書かせていただきました。
CSS設計を導入してから7年以上経ちますが、普段使用していないCSS設計の理解と誤解が解けました。
さて、ようやくスタート地点。さて、これを踏まえてどうやって運用レベルまで落とし込み、現状打破できるか。頑張ります!