2015年11月28日 新しく関連する記事を書きました
おれおれCSS設計・コーディングガイド2015冬の陣
http://qiita.com/nekoneko-wanwan/items/10827552ad705a2d8d52
##はじめに
ここ最近では、cssを管理・設計するという概念(手法?)を意識してサイトを制作することが多くなってきました。
色々な記事を参考にしつつ試行錯誤してきましたが、ようやく自分なりのルールが決まってきた気がします。
一度今の自分のcss設計・構成案についてまとめてみたいと思いますので、この投稿が何かしらの参考になれば幸いです。
※一人で最初から最後までコーディングをすることが多いので、現状分かりづらい箇所もあるかと思います。どんどん改善していければと思います。
##設計において意識していること
- 壊されにくい
- 予測しやすい
- 拡張しやすい
###壊されにくくする
cssは簡単に上書きできてしまいます。ルールを設けることで少しでも予期せぬ不幸を防ぐことを目指します。一度壊れてしまうと取り返しがつかないなぁと、最近こんなソースを見て改めて思いました。
#id1 #id2 #id3 > p {
!important;
}
ただし必ず壊されないようにすることは困難ですし、あまりガチガチにルールを決めてしまっても開発効率が落ちるので、バランスが難しいです。
###どこで何が設定されているか予測しやすくする
以前はcommon.cssだのstyle.cssだのに共通するものを全てまとめていたりしましたが、どんどん肥大していくうちにソース元を辿るのが大変になっていました。devtoolを使わなければまず辿りつけず、全体像が把握できなくなったものです。
この指定はここにある、とすぐにある程度予測できるだけでずいぶんストレスが無くなりました。
###拡張しやすい
後から追加したスタイルが上手く適用されなかったり、もしくは余計なところにも影響してしまったり...。設計とルールが上手く機能していないと気軽に追加することもできなくなってしまいます。cssに合わせてhtmlのタグを決めていく、という本末転倒な事態にもなりかねません。ルールを守っている限りは安心して開発できることを目指します。
##ファイル構成
###全体
-root/
├ _src/scss //開発用scss
└ deploy/common/css/*.css //コンパイルされたcss
###_src/scss
SMACSSとは
・cssのルールを以下のように分類し考え方やルールをそれぞれ決める
・「ベース」「レイアウト」「モジュール」「状態」「テーマ」
BEMとは
・要素をBlock、Element、Modifierいずれかに当てはめて命名し構成を作成
・Blockはそのまとまりの親となり、Elementは子どもとなるイメージ
・Blockは必須だがElementは任意
・Elementを作成する場合は必ず親の下に属し、単独では存在しない
・ModifierはBlockやElementの状態がデフォルトから変わったもの(向きを変えたり、サイズを変えたり...)
scss/
├ lib/ //外部のライブラリやプラグインを格納(他に依存しないもの)
│ └ _*.scss
│
├ component/ //そのプロジェクトに関係なく、再利用できる汎用性の高いパターン
│ ├ _utility-module.scss //汎用的に使い回すモジュール ※Blockの接頭辞にu-を付与
│ └ _utility-single.scss //汎用的に使い回す単体class ※Blockの接頭辞にu-を付与(乱用注意)
│
├ foundation/ //そのプロジェクトの基本的な設定やベースとなる下地をまとめたもの
│ ├ _compass_prefix.scss //compassでのprefix
│ ├ _setting.scss //グローバル変数
│ ├ _abstract.scss //関数・mixin
│ └ _base.scss //主に要素セレクタに対する指定(reset.cssを含むことも)
│
├ project/ //そのプロジェクトで使用する共通パターン
│ ├ _layout.scss //レイアウト(機能と構成要素が固有のもの)に対する設定 ※Blockの接頭辞にl-を付与
│ ├ _module.scss //プロジェクト固有の汎用モジュール ※Blockの接頭辞にm-を付与
│ └ _parts.scss //特定のページでのみ使うものをまとめたい場合 ※接頭辞は付けない
│
├ style.scss //全ページで読み込むスタイル。各パーシャルファイル(_*.scss)をimport
└ *.scss //特定のページでのみ使うスタイル ex)company.scssなど ※接頭辞は付けない
//分けるほどの分量が無ければ_parts.scssにまとめてしまう
##分類する各ファイルについて
###_layout
- ヘッダー・フッター・グローバルナビ...など、機能と構成要素がプロジェクト固有のスタイルをまとめる
- classの接頭辞に l- を付けることで衝突を防ぎつつID的な意味合いを持つことを示す
- 具体的にそのclassが何を表しているかが分かる セマンティックな名前 が望ましい
ex) .l-topic-path, .l-header, .l-g-navi, .l-main-column
<補足>
SMACSSでは同ページで複数存在できるものでもlayoutとすることもありますが(グリッドレイアウトなど)、ここではシンプルにIDが設定できるかどうかをlayoutの判断基準としています。そのため同ページに複数存在しうるものは、_moduleとみなすこととします。
###_module
- プロジェクト内でのみ汎用的に使いまわすスタイルをまとめる
- classの接頭辞には m- を付けることで衝突を防ぎつつ、プロジェクト内独自のモジュールであることを示す
- 使い回す事を前提としているため、セマンティックを犠牲にしてでも やや抽象的 にする
ex) .m-item-list1, .m-head-type1, .m-article-list, .m-img-list1, .m-btn
###_utility
- どんなサイトでも使えるスタイルをまとめる(どんどん機能を改善し使いまわせる)
- classの接頭辞には u- を付けることで衝突を防ぎつつ、プロジェクトに左右されない汎用的なモジュールであることを示す
- 最小限の粒度を持ち、特定の目的に使用されるパーツのため、なるべく 機能を端的に表す 名前にする
- _utility-module: ある程度まとまりを持ち、BlockとElementが存在するもの
- _utility-single: 単体で動作する例外的なもので非常に便利だが、なるべく使用しないようにしたい
_utility-module ex) .u-grid, u-media, u-grad-btn
_utility-single ex) .u-text-right, u-mt10, u-ng-mt10
--- ▲ここまでstyle.cssとしてコンパイルし、全ページに読み込む▲ ---
*.scss or _parts.scss
- 特定ページでのみ使用するスタイルをまとめる
- classには接頭辞を付けず、汎用性は無いものであることを示す
- 具体的にそのclassが何を表しているかが分かる セマンティックな名前 が望ましい
- 個々のscssの分量が少ない、もしくは出力するstyle.css一つにすべて含めたい場合は、project/_parts.scssとしてまとめても良い
ex) .company-photo, .top-slider
基本ルール
##IDによるスタイル設定の禁止
- 「IDは機能」「CSSは装飾」と用途別に分別する
- 実質ID的な使い方をする_layoutでも必要以外では避ける
なおここでのIDが必要とは、ページ内リンク、jsのフックにする、などスタイル設定以外での使用を指す
##要素セレクタ指定の制限
- 要素セレクタに対するスタイルは以下の場合を除き、なるべく避ける
・スタイルのベースとして設定する(aタグやinputなどが多い)
・子要素に必ず特定のタグを使う(li, a, img, inputなどが多い)
/* ダメ */
.block div {}
.m-item-list h1 {}
/* OK */
.block {}
.block__element {}
.m-item-list {}
.m-item-list__head {}
/* OK(許可) */
.m-item-list li {}
.u-media img {}
##命名規則(BEMに基づく)
- BlockとElementはアンダースコア2つ __ でつなぐ
- Modifierは is-、has- などを接頭辞としたマルチクラス方式を採用
- 単語間の区切りは キャメルケース もしくは ハイフン でつなぐ(統一されていればどちらでも良い)
ex) .searchBox > .searchBox.is-vertical , .searchBox__elem
ex) .search-box > .search-box.is-vertical, .search-box__elem
##ElementのElement(孫)の扱い
- 階層化が明確ではなくなるが、可読性を優先し命名上はエレメントを並列にする
- ※個人的に一番悩ましいところでもあります
- ex) itemをBlock, boxとheadをElementとした場合
/* ダメ */
.item {}
.item__box {}
.item__box__head {}
/* OK */
.item {}
.item__box {}
.item__head {}
##scssファイルへの記述について
Elementは並列に記述する
- 要素セレクタや擬似要素以外では、必要以上の階層化は避ける
// ダメ
.block {
.block__element {
}
}
// OK
.block {
&.is-modifier { // Block自身のModifier
}
}
.block__element {
&.is-modifier { //Element自身のModifier
}
.block.is-modifier & { //BlockのModifierによるElementの変化
}
}
Modifierには直接設定しない
- マルチクラスを採用しているため重複する恐れがある
- 逆にマルチクラスを採用しない場合は、直接設定すべき
// ダメ
div.is-modifier {
display: none;
}
##ID,classの記述順
- 一つのタグにIDや複数classを記述する場合は、先頭から以下を優先順位とする
- ただしテンプレートエンジンなどで順番が自動設定され、調整が手間である場合は妥協しても良い
- BlockもしくはElement
- Modifier
- _utility
- ID
<div class="block" id="hoge">
<div class="block__element is-modifier u-mt10" id="fuga">
</div>
</div>
モジュール化を検討する目安
- 接頭辞を付けないものに対し、モジュールとして汎用性を持たせるかどうかの判断基準
- ページを跨いで同じようなコードが 3回 以上存在した場合
- 汎用的なclassを 3つ 以上、htmlタグに付与している場合
- _utility-moduleが沢山ついているのであれば、一つの_moduleにまとめられないか考える
scssに追加するまでの大まかな流れ
■1: その要素は汎用的な(ページを問わず自由に使用できる)ものか
→ Yes: 2へ
→ No : 4へ
■2: その要素はプロジェクトのデザインや設定(変数や関数)に依存するものか
→ Yes: Bになります
→ No : 3へ
■3: その要素は他要素と組み合わせて使用されるものか
→ Yes: Dになります
→ No : Eになります
■4: その要素は全ページ、もしくはほどんどのページで使用されるものか
→ Yes: 5へ
→ No : Cになります
■5: その要素にはIDを付与できるか(1ページに1つ)
→ Yes: Aへ
→ No : 汎用的であるとみなし、2へ戻る
----------------------
A. レイアウトです。l-を付けて、project/_layout.scssに入れましょう
B. プロジェクト汎用モジュール・スタイルです。m-を付けて、project/_module.scssに入れましょう
C. プロジェクト固有モジュール・スタイルです。接頭辞は付けずに、project/_parts.scssに入れる、もしくは*.scssとして分離させましょう
D. 汎用モジュールです。u-を付けて、component/_utility-module.scssに入れましょう
E. 汎用スタイルです。u-を付けて、component/_utility-singleに入れましょう
※ただし同じ要素に3つ以上付ける場合は、BやDとして考えましょう
----------------------
おわり
現在はこのような構成で作成することが多いですが、当然改善すべき箇所は沢山あると思っています。
(特にマルチクラス方式は微妙かな、と思うこともあります。。。)
何より大切なのは、ただ漫然とcssを作成するのではなく、設計という考えを意識しルールを決めること、もっと良い方法はないかと常に疑問を持ち開発全体の効率をあげようと精進することではないかと考えます。