Web制作者のためのCSS設計の教科書 モダンWeb開発に欠かせない「修正しやすいCSS」の設計手法 - インプレスブックスを読んだので、読書メモです。
Ch1
スタイルは打ち消さずに追加する
- スタイルを打ち消すためのスタイルを定義すると、記述が増えてしまう
- 共通して使えるスタイル + 特定の場合のみのスタイルの2つを定義して、組み合わせによってスタイルを指定するようにすると、記述がシンプルにできて良い
Ch2
詳細度を不用意に高めないための方法
- 要素セレクタを省略する (classセレクタのみを使うなど)
- セレクタを短くする
- CSSのidセレクタは避ける
セレクタを短くする
-
セレクタはマークアップの構造に従う必要はない
- e.g. ul要素の直下はli要素だが、セレクタではその通りに書く必要はない
Ch3
コンポーネントとは
-
コンポーネントであるためには、「関心の分離(Separation of concerns)」をしている必要がある
- 機能と振る舞いを明確に分離するということ
- CSSのコンポーネント化でいうと、HTMLのマークアップ構造とCSSのスタイリングが分離できていることなどが該当する
-
分離を実現するには、カプセル化が重要
- カプセル化とは、コンポーネントの構造やデータなどを隠し、外部からは許可された操作のみを受け付けること
- また、内部の仕様変更が外部に影響しないようにすること
- カプセル化にはスコープが重要になるが、CSSにはグローバルスコープしか存在しないため、実現がむずかしい
OOCSS
- Object Oriented CSS の略
- オーオーシーエスエスと読む
OOCSSの原則
- 構造と見た目の分離
- コンテナーとコンテンツを分離
構造と見た目の分離
-
コンポーネントの基本構造と、具体的なルール・機能を分離する
- 基本構造のためのスタイルと、具体的なルール・機能に対するスタイルを別に定義して、組み合わせでスタイルを当てる
- 基本構造に変更があった場合には、基本構造のスタイルを変更するだけで全体のスタイルを変更できるため修正の手間が減らせる
- また、新たに具体的なルールを増やす時も、具体的なルールだけを追加すればいいので追加しやすい
コンテナーとコンテンツを分離
-
場所に依存しないセレクタを書く
- 場所に依存しないことで、置き場が変わったとしても大きな修正が不要になる
SMACSS
- Scalable and Modular Architecture for CSS の略
- スマックスと読む
-
OOCSSのコンセプトをベースに作られたCSSの設計手法
- パターンを抽出しやすいようにカテゴリを用意している
SMACCSのカテゴライズ
- Base
- Layout
- Module
- State
- Theme
Base
- ブラウザおよびプロジェクトにおける、各要素のデフォルトのスタイルを定義する
- reset.cssやnormalize.cssなどの要素のスタイルを初期化するライブラリもBaseカテゴリに該当する
-
基本的にBaseカテゴリで定義したものは変更しない (変更すると多くの要素に影響を与えてしまうため)
- 特に、width, margin, paddingなどのスタイルは持たせない方がいい
Layout
-
構成の大枠、セクションを作る要素へのスタイルを定義する
- e.g. ヘッダー、フッター、コンテンツエリア、サイドバー
- これらの要素はページに1つしか存在しないはずなので、IDセレクタを使っても良い
- クラス名の接頭辞に
l-*
をつけることが推奨 (Layoutカテゴリのコンポーネントであることを明示するため)
Module
- Layoutパターンを除く、ページを構成するほぼすべての要素のスタイルを定義する
State
-
JSによる制御などで切り替わるような、状態を表すスタイルを定義する
- e.g. 要素を一時的に隠すために
display: none;
を適用させるための、is-hidden
クラス
- e.g. 要素を一時的に隠すために
- クラス名の接頭辞に
is-*
をつけることが推奨 -
他のカテゴリと違い、クラス名に対象のコンポーネントの名前も含めることが推奨
- e.g. タブの有効状態を表すスタイルなら、
is-tab-active
- 汎用的な名前(e.g.
is-active
)にすると、他の意図で使われた場合にスタイルが汚染される可能性があるため - 何のためのクラスであるかを明確に表すため
- e.g. タブの有効状態を表すスタイルなら、
-
!important
を使うことが推奨- Stateは必然的にスタイルを変更させることが目的であるため
-
メディアクエリもStateに該当する
- コンポーネント単位でメディアクエリを定義した方がメンテナンス性が高くなる
- ただし、同じメディアクエリを宣言する箇所は増える (1箇所のメディアクエリにまとめて定義しなくなるため)
Theme
-
テーマを切り替えたりするような機能が求められる時に、そのスタイルを定義する
- Stateよりも広い範囲のスタイルの変更のイメージ
- e.g. カレンダーアプリで、ユーザー側の設定によってテーマが切り替わる場合
- クラス名の接頭辞に
theme-*
をつけることが推奨
Moduleの命名規則
-
ベースとなる基本構造に、具体的なスタイルを追加する時は、ベースのコンポーネント名を名前空間として継承した命名にする
- e.g. ベースが
alert
なら、成功時のスタイルは、alert-success
- e.g. ベースが
-
上記のメリット
- クラス名の重複によるスタイルの競合と汚染のリスクを減らせる
- クラス名を見て、どのコンポーネントの拡張かがわかる
- セレクタの詳細度を減らせる
BEM
- Block Element Modifier の略
- ベムと読む
- ページを構成する要素(オブジェクト/コンポーネント)を3つに分類して考える設計手法
Block
-
ベースとなる基本構造のこと
- e.g. SMACCSで出てきた、
alert
- e.g. SMACCSで出てきた、
- Element, Modifier はこのBlockを基点として命名される
Element
-
Blockを構成する要素
- e.g. SMACSSで出てきた、
alert-title
- e.g. SMACSSで出てきた、
-
命名規則として、
__
(アンダースコア2個)をつける (Modifierとの役割を明確にするため)- e.g.
alert__title
- e.g.
Modifier
-
BlockまたはElementのバリエーション違いの要素
- e.g. SMACCSで出てきた、
alert-warning
- e.g. SMACSSのStateも該当 (
is-tab-active
)
- e.g. SMACCSで出てきた、
- 命名規則として、
_
(アンダースコア1個)をつける (Elementとの役割を明確にするため)
MindBEMding
-
BEMの命名規則のエッセンスを取り入れつつ、実際の区切り文字を変更したもの
- Elementは
__
(BEMと同じ)、Modifierは--
(ハイフン2個)というルール
- Elementは
-
ElementとModifierの関係性を表すことができる
- Elementは、Blockと「親子」の関係にあるため、記号も下がっている形状のアンダースコア
- Modifierは、BlockまたはElementのバリエーション違いとして「対等」の関係にあるため、記号は中央に直線のハイフン
-
コンポーネントの関係を示す以外の用途に、ハイフン(1個)を使える
- e.g. セレクタが複数単語の場合に区切り文字にできる (
.search-box
)
- e.g. セレクタが複数単語の場合に区切り文字にできる (
Ch4
Rule of Three
- 3回繰り返したものは、パターンとして成立する
- 「時期尚早な最適化はすべての悪の根源である」が、実際にどのタイミングで最適化すればいいのかを表したの考え方
-
CSSのコンポーネント化のプロセスに落とし込むと、次のような考え方になる
- 初めてそのパターンが登場した時は、再利用することは考えない
- 再び遭遇した時も、コンポーネント化したい気持ちを抑える (まだ必要なパターンを理解できてない可能性が高いため)
- 3度目に遭遇した時は、パターンとしてわかっていること・わかってないことを整理して、コンポーネント化するためのリファクタリングを行う
- 逆に3度目を逃すと、影響範囲が大きくなりすぎてリファクタリングが行いにくくなる
- 視野を広げると、「3つのプロジェクトで使えたコンポーネントなら、他のプロジェクトでも使える」とも考えられる
SOLID CSS
- オブジェクト指向設計の原則である、SOLIDをCSSに適用したもの
単一責任の原則 (Single Responsibility Principle)
-
オブジェクトは1つの責任を持つべきであり、その責任は他に影響を与えてはいけない
- OOCSSの原則はまさにこれである (構造という責任と、見た目という責任を分離して設計する)
開放/閉鎖の原則 (Open/Closed Principle)
-
コンポーネントのベースになるルールは、上書き・拡張がしやすくあるべき(開放的)で、できる限り変更するべきではない(閉鎖的)
- ベースとなるルールは最小限必要なものに止めながら、Modifierによって拡張できるようにする方が利便性が高くなる
コンポーネントにおける再利用
- コンポーネント単位で再利用することを意味する
- そのため、2つ以上のコンポーネントで同じスタイルが使われている場合でも、別のコンポーネントのスタイルを使い回すようなことはしない
-
対応方法は2つ
- それぞれのコンポーネントごとにスタイルを定義する
- SMACCSのThemeのように、装飾用のコンポーネント定義して使う
コンポーネントの設計・実装パターン
ボタン
- 対象となるHTMLの要素が限定されない(buttonタグの場合もあれば、aタグの場合もあるなど)ので、CSSのセレクタに要素を含めない
button要素
- ユーザーエージェントごとのデフォルトスタイルを初期化する必要があり、方法が2通りある
/* SMACCSでいうBaseカテゴリとして、button要素のデフォルトスタイルを初期化する */
button {
border: 0;
margin: 0;
padding: 0;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: transparent none;
font-size: inherit;
}
/* button要素のスタイルは残したい場合は、ボタンコンポーネントのスタイルで初期化する */
button {}
.btn {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
margin: 0;
font-size: inherit;
/* 以下、ボタンコンポーネントのスタイル */
}
a要素
- button要素よりも多くのケースで使われるため、a要素自体のスタイルを変更するのはやめた方がいい
.btn {
text-decoration: none;
color: inherit;
/* 以下、ボタンコンポーネントのスタイル */
}
コンポーネント化における「何がわかっていて、何がわかっていないか」
-
わかっているものはコンポーネントで定義、わからないものは使われる場面で対応する
- e.g. ボーダーや背景色、paddingの値は、コンポーネントが使われる状況に関わらず変わらないベースのルールがわかっている (= コンポーネントで定義する)
- e.g. 幅については、コンポーネントが使われる状況によって異なるのでわからない (= コンポーネントで定義しない)
- e.g. 必ず同じ幅になることがわかっている場合 (= コンポーネントで定義する)
-
わからないものの対応方法は2つある
- Modifierを使う
- コンポーネントが使われるブロックに持たせる (こちらの方がbetter)
グリッドレイアウト
-
カラム幅は、100%をカラム数で割って算出する
- 端数は適当に切り上げる
- 厳密に端数を処理したい場合は、Foundationが行なっているようなfloatを使って余白を埋める方法などがある
ガター
- カラム間の余白のこと
- グリッドでぴったりとブロックを埋める(ガターがない状態)には、
box-sizing: border-box;
を使うと良い - ガターを持たせる場合は、paddingで左右に余白を作ったModifierを作って、それを必要に応じてつければいい
position: absoluteを使った中央配置
-
上下/左右の双方に値を持たせると、配置のエリアが指定間に広がる
top: 0; bottom: 0;
の場合: relativeの要素の高さいっぱいに配置領域ができるtop: 50%; margin-top: -heightの半分
のやり方よりも修正の手間がかからず、メンテナンスがしやすい
Ch5
マルチクラス
- 1つ1つのコンポーネントをCSSのクラスとして、それを組み合わせることでUIを作る手法
- UIが複雑になるほど、HTMLに記述するクラスが増える
シングルクラス
- 単一のクラスでUIを作る手法
- UIが複雑になってもHTMLの記述するクラスは1つだが、マルチクラスに比べるとクラスの定義が多くなる
- OOCSSを意識しながら、Sassの
@extend
を使うと実装しやすい -
セマンティックなクラス名をつけることができる
- その反面として名前と一致しない要素にスタイルを再利用できないデメリットもある
% (Sassの記法)
- Placeholder Selector
- セレクタに指定すると、CSSには出力されなくなる
- シングルクラスで、コードの実装中のみ欲しいセレクタなどに指定すると良い