OOCSS / BEM / SMACSS / FLOCSS は CSS コーディングルールです。ルールを標準化してメンテ性・チーム作業効率を高めていこうという動きで作られたものです。
これら以外にもありますが有名なのはこのあたりで、最近真面目に調べていなかったのでメモです(真面目に調べたとは言っていない)。
自分以外の誰が見てもわかり、メンテとかで CSS を編集しようと思った時に辛くならないようにルールを適用しておくと、モテるかもしれません。
キャンペーンやランディングページのコーディングは、なるべく早く終わらせたいのでこの辺は無視することが多かったですが、サービス開発とかで UI の微調整が何度も入るようなもの、自分以外でも作業するようなものに関してはルールを定義しておいてもよいかもしれません。
① OOCSS
「CSS をオブジェクト指向で考える」
まず Container (容器・箱) と Contents (中身) を分解します。
.login-form
(Container) と .heading
/.input
/.btn
(Contents) がそれぞれネストされないようにします。
.login-form > .btn
と依存せず、.btn
を独立させることでいろんなところで使いまわせるようにします。
そして Structure (構造) と Skin (ガワ) を分解。
.btn
と .btn-red
/.btn-medium
など
シンプルなルールなので、厳格になりすぎず扱いやすいので、よく使われるイメージ。Bootstrap なアプローチです。
例えば: OOCSS を適用する前の CSS
#main h2 { } /* なんか大きい[level1] */
#main .login .header h2 { } /* なんか大きい[level1] */
#side-menu h2 { } /* サイドメニューの上なのでちょっと小さい[level2] */
.user-profile .name h2 { } /* 実は SEO 的に入れててビジュアル的には小さくしたい[level3] */
h2
の指定がいくつもある割に、おかれている場所が違うので実は周辺要素との重みの違いや、全て同じ大きさの見出しレベルで扱うものでないことが多かったりします。必然的にスタイルが実はそれぞれ違うことが多かったりします。なのでそれを囲った div
を作ってオブジェクトとして再定義します。
OOCSS 化した CSS
.heading1 { } /* [level1] */
.heading2 { } /* [level2] */
.heading3 { } /* [level3] */
するとレイアウトとは独立されたオブジェクトとして捉えることができます。
- OOCSS (http://oocss.org/)
② BEM
「クラスを 3 つの概念に分けて命名する」
3つの概念は Block (かたまり)、Element (要素)、Modifier (修飾) で、.article__title--type_primary
というようなクラス名にします。
<ul class="gnav"> <!--Block-->
<li class="gnav__item"></li> <!--Element-->
<li class="gnav__item"></li> <!--Element-->
<li class="gnav__item gnav__item_state_current"></li> <!--Modifier-->
</ul>
セパレーター
Block__Element--key_value
Block と Element の間はアンダースコア _ 二つ。 Block と Modifier または Element と Modifier との間はハイフン - 二つ。 Modifier は key と value で分解してしてアンダースコアで区切ります。 各語はハイフン - で区切ります。
クラス名例
.comment__content
.aritcle__title--state_error
.widget__title--font_small
.search
.search__input
.search__button
.site-logo--xmas //一時的な変更は Modifier
メンテ性、チーム作業効率が重視されたルールです。
例えば .entry-page .title
とかにしてしまうと、別のページに HTML をコピペしたくなった場合、このスタイルが使えません。BEM であれば .entry-page__title
というような感じのクラスになっているので、一目でそのタグがどこのブロックに属しているかがわかり、移行作業がスムーズになります。
特殊っぽい書き方になるのでクラス名が長くなったり違和感は結構残りがちですが、Block の名前を短くすることを意識するとよさそうです。
(OOCSS でも結局 .btn .btn-primary .disbled
みたいなクラスになることもあるので、変わらないといえば変わらない)
- BEM (https://en.bem.info/)
- MindBEMding (http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/)
③ SMACSS
「5 つの主要カテゴリで分解、スタイル適用する」
ベース、レイアウト、モジュール、ステート、テーマの 5 つで分解します。 だいたいレイアウトとモジュールで定義して、ステートは JavaScript で状態が変化するときのパターンだけ使い、テーマはローカライズ時ぐらいで、あまり使いません。
ベース
CSS リセット、ノーマライズ、ベースのスタイライズなどを行います。(reset.css, helper.css ...) クラスでなく、要素や属性など基礎的なものだけに適用します。
要素 (body, a など) 擬似クラス (a:hover, p:before など) 属性 (inputtype="text" など)
ここで色々スタイライズしちゃうと後で細かいビジュアルのアタッチが面倒になるので最低限にとどめておくのが良さそうです。
レイアウト
画面の大枠な位置、配置の指定をします。 .layout-, .l- などの接頭辞をつけます。 画面やペイン、エリアなどのレイアウトを定義します。(.layout-header, .layout-footer, .layout-nav, .layout-main, .layout-sub, .layout-modal ...) フォント、色は以降のルールで指定します。
.l-header { border-top: 1px solid black; }
.l-footer { margin: 10px 0 0 0; }
.l-main { float: left; width: 80%; }
.l-sub { float: right; width: 20%; }
モジュール
部品ごとのスタイルを指定します。(.logo, .tabs, .box, .header, .footer, .list, .form, .cart, .search, .comment ...
)
モジュールの位置がビジュアルのグリッド上に存在していて、グリッドを抽象化できる場合はレイアウトで位置情報を定義できるとよさそうです(layout-grid-04 など)。
/* ベースルール */
body { margin: 0; padding: 0; }
/* レイアウトルール */
.l-container { width: 950px; margin-left: -10px; *zoom: 1; }
.l-container:after { clear: both; content: ""; display: table; }
.l-grid-01, .l-grid-02, .l-grid-03, .l-grid-04, .l-grid-05, .l-grid-06, .l-grid-07, .l-grid-08, .l-grid-09, .l-grid-10, .l-grid-11, .l-grid-12
{ float: left; margin-left: 10px; }
.l-grid-01 { width: 60px; }
.l-grid-02 { width: 140px; }
.l-grid-03 { width: 220px; }
.l-grid-04 { width: 300px; }
.l-grid-05 { width: 380px; }
.l-grid-06 { width: 460px; }
.l-grid-07 { width: 540px; }
.l-grid-08 { width: 620px; }
.l-grid-09 { width: 700px; }
.l-grid-10 { width: 780px; }
.l-grid-11 { width: 860px; }
.l-grid-12 { width: 940px; }
/* モジュールルール */
.box {
padding: 20px 0;
background: #990000;
color: #fff;
text-align: center;
}
ステート(状態)
モジュールの「状態」を指定します。(.is-disabled
, .is-active
, is-error
, is-hidden
...)
主に JavaScript (ハンバーガーメニュー ON/OFF 表示など)で操作されるクラスを指定します。
.btn-disabled
(モジュールでの指定) / .is-disabled
(ステートでの指定)
テーマ
大枠の色の指定、フォントサイズの微調整などを行います。(.theme-border
, .theme-background
, .theme-japanese
, .theme-english
...)
使用する機会は少ないでしょう。
構成例
├── app.scss
├── base
│ ├── _base.scss /* ノーマライズ、基本スタイライズ */
│ └── _mixins.scss
├── layout
│ ├── _layout.scss /* 汎用レイアウト: ex. l-container */
│ ├── _top.scss
│ ├── _product.scss
│ ├── _nav.scss
│ └── _signin.scss
└── module
├── _module.scss /* 汎用モジュール: ex. box */
├── _logo.scss
├── _footer.scss
├── _product.scss
├── _nav.scss
└── _signin.scss
その他のルール
ID や class を指定せず、セレクタのみでスタイル適用してください。ID セレクタは複数利用できない、CSS が複雑になりやすいとのことから非推奨で、主に class だけで指定します。(JavaScript やページ内アンカーとして利用するのは別)
クラス名を「つけるつけない」について、li
, a
などはクラス名をつけず、div
, span
などはつけるようにします。
基準はどういうものに使われているか意味がわかるかどうかで(セマンティックかどうか)、前者はリストやリンクということがわかりますが、後者は汎用要素なのでクラス名をつけても良いということになります。
li
で階層が深くなる場合はサブ階層の定義に >
を使います。
見出しは h2
が h3
に変わることもあるのでクラス指定された div
(.heading2
など) で囲うとエロいです (OOCSS 参照)。
汎用レイアウト、汎用モジュールの実態は、ベースルール上の _mixins.scss に定義する方がよいかもしれません。
- SMACSS E-book 日本語 (http://shop.smacss.com/products/smacss-e-book)
- SMACSS 読んだ (http://chroma.hatenablog.com/entry/2013/07/22/120818)
④ FLOCSS
「OOCSS, SMACSS, BEM, SuitCSS, MCSS の考え方を取り入れた比較的新しいやつ」
最近のマークアップ界隈では OOCSS / BEM / SMACSS をそれぞれ取り入れてイイトコ取りした感じで設計していることが多いらしく、それを体系化してくださったものを FLOCSS として公開されている、というもののようです。 (SuitCSS, MCSS も別の CSS ルール)
命名規則
BEM の拡張 MindBEMding (http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/) をそのまま取り入れる
.p-articles__title
JavaScript で状態を変更するような場合は SMACSS 同様 is-
接頭辞を使用します。(class="c-button is-active"
)
構成
1. Foundation - reset/normalize/base...
2. Layout - header/main/sideber/footer...
3. Object
3-1. Component - grid/button/form/media...
3-2. Project - articles/ranking/promo...
3-3. Utility - clearfix/display/margin...
Foundation
リセット、ノーマライズ、基本設定など、SMACCS でいうところの「ベース」です。
また _mixin.scss
や、_config.scss
などもこのレイヤーに配置することが多いようです。
(_config.scss
などに色名やフォントサイズなどの変数を定義する)
Layout
#header, #contents
あるいはプロジェクト共通のコンテナを定義します。
汎用的なレイアウトルールを定義する場合、Sass (SCSS) で書かれている場合は、.layout-grid-12
を mixin として作成しておき、 Layout で呼び出しクラスを定義しつつも include して使用します。
Object - Component
Object はプロジェクト中で繰り返されるパーツで、Component / Project / Utility で分類します。
Component は再利用できる小さな単位のモジュールです。c-
接頭辞をつけます。
(.c-button
, .c-button__danger ...
)
Object - Project
プロジェクト固有のオブジェクトです。p-
接頭辞をつけます。
(.p-gallery
, .p-profile
, .p-article
...)
Object - Utility
Component、Project で解決が難しい、あるいはスタイルの微調整につかいます。ユーティリティ、ヘルパーなどを入れます。
(.u-margin-top-10
, .u-clearfix
...)
構成例、その他のルール
公式の readme に詳しく書いてくださっています。
- FLOCSS (https://github.com/hiloki/flocss)
まとめ
汎用的なウェブのビジュアルを持つ CSS フレームワークを作りたいなと思って振り返ってみたのですが、やっぱりあれこれ考えることが多くて挫折うけあいです。
どのルールにするか/ルールの解釈での宗教戦争(インデント、アンダースコアvsハイフン、エディタ、言語同様)は適宜行われることでしょう。
結構几帳面な人じゃないと辛いので、lint とか使う前提にした方がいいかもしれません。(つらそう)