HTML、CSS設計方法!OOCSS、SMACSS、BEMについて
「良いHTML、CSS」の基本ルール
CSSといえど、プログラミング言語であることは間違いなく、とりわけ「CSS」だからというものはありません。
どの言語においても「変更に耐えうるコード」が一つの鍵となってきます。 特にCSSは軟弱な言語であり、セレクタの書き方、およびHTMLのクラス属性の命名など要件を満たすには難しい部分はあると思います。
そのような部分を明文化して、備忘録として残せれば望外の喜びだと思い、本記事を執筆しようと思いました。
アジェンダ
以下のようなアジェンダになっています。
一般論的な話
以下の要件の時にコードを書くこと、または変更すること、削ることになります。
- 要件の追加
- バグの修正
- 設計の改善
- リソース利用の最適化
1. 要件の追加
HTML要素が追加されたときに限らず、レイアウトを現行のものから変更したいときなどに要件が発生します。
例えば「ハンバーガーメニューを右上にしたい」などといったものです。
コード自体は振る舞いを記述するものであり、たとえ要素の場所の変更でも要件が追加されたとも考えることができます。
2. バグの修正
要素の場所を変更したいということは、コードの振る舞いを変更したいのと同値です。
つまり、「バグである」と考えることができます。
3. 設計の改善
CSS自体の構造修正です。
例えば以下のようなHTMLに対して二つのCSSを書くことができます。
<div id="main-page"> <div class="content-container"> <p class="content"> This is a blog </p> </div> </div>
これに対して
CSSは
div > div > p { color: red; }
とも書けますし、
.content-container > content { color: red }
とも書けます。
どちらが良い方法かは、この記事を読めばわかると思います。
しかしながら、前者から後者に書き換えをしても何も振る舞いは変わりません。
4. リソース利用の最適化
グラフィカルな演算では、GPUを使ったりするイメージがあるでしょう。
ブラウザの仕組みを少し解説すると
DOMとCSSOMをレンダリングしてレイアウトを決めてペインティングをする
という流れになっています。Paintingが大きなコストをしめるのは問題ではないですが、DOMの変更を伴わないGIFやcanva
要素、そしてCSSアニメーションプロパティが関与してきます。
animation
や@keyframes
によって簡単に使用することができて、非常に一般的ですが制御しきれないという過ちが多いのも事実です。したがって、コストを著しくあげることになるケースがあります。
もちろん、このようなケースではリソースが主眼点であり、振る舞いもコードの構造も変更されません。
HTML、CSSの設計のポイント
HTMLに依存したCSSをしないこと
多くの場合、デザイン変更でもっとも重要なのは構造変更、つまりHTMLを書き直すシチュエーションが多いでしょう。
そのようなときにHTMLを変更すると同時にCSSも変更しなければならないのは、しんどいでしょう。 何か一つHTML要素を変更すると、それ以上に大幅にCSSを変更しなければなりません。
ある程度、HTMLとCSSの結合度の低いコードを書くことが大事です。
例えば以下のようなHTMLコードがあるとします。
<div> <h2>ブログです</h2> <div> <p>hogehoge</p> </div> </div>
そのようなときに以下のようなCSSを書いたとしましょう。
div > h2 { border: 1px gray solid; font-size: 28px; } div > div > p { font-size: 12px; }
すると、以下のよう変更があったときが耐えられません。
<div> <div> <h5>Subtitle</h5> <div> <div> <h2>Title</h2> <div> <p>hogehoge</p> </div> </div> </div>
そのようなときに
<div class='title-container'> <h2 class='title'>ブログです</h2> <div> <p class='content'>hogehoge</p> </div> </div>
としてCSSはclass属性に結合を持つ
.title { .... } .title-container > content { ... }
とすればCSS側は変更する必要がなくなります。まとめるとh2などタグでスタイル付けをするのは好ましくないでしょう。
絶対値でほとんどの値を定義している
以下のようなケースを考えます。
<div class='product'> <h2 class='product-name'>はてなブログ</h2> <div class='product-details'> <p class='product-description'>いいブログがかけます</p> </div> </div>
そして以下のように絶対値で何もかも定義したら、あとあと面倒です。
ダメCSSコード❌
.product-name { font-size: 18px; line-height: 27px; } .product-description { font-size:18px; line-height: 27px; }
しかし、font-size
を大きくするとline-height
も同時に変えなければなりません。そのようなときは
良いコード✅
.product { font-size: 18px; line-height: 1.5 } .product-description { font-size:18px; }
とすることができ、font-size
を変更してもline-height
をいじる必要がありません。特にproduct-description
はproduct
から継承されるため、指定すらする必要がありません。ここで1.5
としているのは単位をつけると、その値そのままが継承されてしまうからです。
不要なセレクタが縛っている
不要なセレクタがあると、いくらclass
を定義して頑張った気になったとしてもHTMLに束縛をされます。
例えば
<div class='product'> <h2 class='product-name'>はてなブログ</h2> <div class='product-details'> <p class='product-description'>いいブログがかけます</p> </div> </div>
に対して
h2.product-name { ... }
とするとh2
に対して束縛をされていることになります。
.product-name{ ... }
としても問題はなく、h3
になるなどマークアップに変更点があったとしても機能します。
長いセレクタ
セレクタ>
やでトップレベルからわざわざ指定をすると、構造そのものに束縛を受けることになります。
<div class='product'> <h2 class='product-name'>はてなブログ</h2> <div class='product-details'> <p class='product-description'>いいブログがかけます</p> </div> </div>
の場合に
.product .product-details p { ... }
でなくて
.product p { ... }
とする方が移植性が高く、変更に耐えられます。
設計のアイデア
いくつかHTMLとCSSを設計のポイントを解説していこうと思います。
0. 最初に知っておきたい現状
これからCSSのOOCSS、BEM、SMACSSの3つの設計パターンを解説していきますが、世界ではどのような位置付けなのでしょう。
Google Trendで見てみましょう。
圧倒的にBEMがよく使われているのではないかと推測することができます。
1. OOCSSを実践する
OOCSS(Object Oriented CSS)とは、オブジェクト指向の考えをCSSに取り込み、変更に耐えられるCSSをデザインするのが目的とするCSSのデザインパターンです。
OOCSSの恩恵としては
- スピード: CSSファイルを小さくできるのでブロックせずにレスポンスを素早く返せる
- スケーラビリティ:
class
属性をうまく使い、変更に耐えうるコードをかける - 効率性: DRYルールで、効率よくコードを書くことができる
- メンテナンスが簡単: HTML要素に変更があっても、完全にすべてCSSを書き換える必要がない
- 可読性: とにかくCSSが読みやすい。
ここでいるオブジェクト(Object)とは、繰り返されるビジュアルパターンです。
ルールは二つあって
- 構造と見た目の分離
- コンテナとコンテンツの分離
です。
1.1 構造と見た目の分離
構造とは、いわゆるユーザーに見えない要素のサイズやポジションなどをいいます。
構造に関するCSSプロパティでいうとどのように配置するべきかを定義する以下のようなものであり
- Height
- Width
- Margins
- Padding
- Overflow
そして見た目に関するCSSプロパティはどのように見えるかを定義する以下のようなものです。
- Colors
- Fonts
- Shadow
- Gradients
例えば以下のような3つの要素があったとします。
<div class="morning-message">Good morning</div> <div class="afternoon-message">Hello</div> <div class="night-message">Good Night</div>
可視化をすると以下の通りです。
このようなHTML要素に対して、以下のようなCSSを定義しています。
.morning-message { width: 150px; height: 50px; background: yellow; } .afternoon-message { width: 150px; height: 50px; background: red; } .night-message { width: 150px; height: 50px; background: gray; }
このような要素だと別々にそれぞれ定義するよりは共通するclass属性greeting
などを定義すると共通要素をつけられます。
まるで共通部分をオブジェクトの特性としてまとめたような感じです。
<div class="message morning-message">Good morning</div> <div class="message afternoon-message">Hello</div> <div class="message night-message">Good Night</div>
コンテナとコンテンツを分離
二つ目の「コンテナとコンテンツの分離」は場所に依存しないCSSを書くということです。
一貫性と、メンテナンス性を提供します。
以下のように.sidebar
のように「場所」に関するスタイルと、.list
のように「コンテンツ」に関するCSSを分離することによって、どこにlist
を配置してもスタイルが崩れることなく、移行することができます。
.sidebar { padding: 2px; left: 0; margin: 3px; position: absolute; width: 140px; } .list { margin: 3px; } .list-header { font-size: 16px; color: red } .list-body { font-size: 12px; color: #FFF; background-color: red; }
SMACSSを実践する
SMACSS(スマックス)とはフロントエンドのスタイルガイドで、OOCSSを元に定義されています。
SMACSSはパターン化のルールを定義しているものです。
- Module: Layoutを構成する単位。
- State: エラーや隠れたりするなど状態が変わるようなものです。
is-
の接頭語をつけます。is-hidden
やis-active-table
のように状態を定義します。 - Theme: テーマを切り替えるなど包括的な機能を実現するものに対して使われます。
ocean-hokorobi
などとテーマをbody
要素などtheme-
の接頭語をつけて設定します。
1. Base: 各要素のデフォルトスタイルを定義
Baseは、各要素のデフォルトを定義します。
例えば、以下のような指定方法があります。
html, body, form { margin: 0; padding: 0; } input [text=text] { border: 1px solid red;} a { color: blue } a:hover { color: green; P
2. Layout: ページをセクション毎に分割する構成単位のスタイル
慣習的にid
を用いて定義をしていきます。
#header, #footer { ... }
などと定義をします。しかし、gridなどは.l-grid
のようにクラス属性を使ってスタイルを指定してきます。
id
を指定しないときは.l-
と接頭語をつけます。
3. Module: Layoutの構成単位
ほぼすべてのパターンがこのModuleになります。
接頭語はm-
やmod--
をつけたりします。
4. State: エラーや隠れたりするなど、状態を示す
例えばis-hidden
のように、隠れた要素にclass属性を定義します。
例えば隠したい要素は
<div class="product"> <div class="message soldout-message"> ... </div> <div> ... </div> </div>
のようにsoldout-message
のようなものをさします。
.message { ... } .soldout-message { color: red; }
5. Theme: テーマルールを指定するもの
わかりやすく、テーマについてである。
例えばHTMLのBodyタグに以下のようにページ全体についてのスタイル付けをしていくものがある。
<body class="theme-red"> ... </body>
CSSは以下の通りである。
.theme-red #menu { backgroud: white; } .theme-red #navi-bar { ... }
のようにします。このようにテーマに関する影響をうけるものはtheme-
と接頭語をつけることが推奨されています。
このようにすることによって、ページ全体を書き換えることができる。
BEMを実践する
BEM(Block Element Modifier)とは、再利用可能なコンポーネットとコードを共通化するためのメソッドです。
開発者に人気の命名規則で、多くのマークアップフロントエンドで取り入れられているものです。
公式サイトは以下の通りです。
特徴として
- 簡単: 命名規則を覚えるだけ
- モジューラ: 独立したブロックとCSSセレクタを使う
- 柔軟性: 簡単に設定できて、再定義できる
が挙げられます。それでは、詳しくみていきましょう。
Block
Blockとは、命名規則の基礎を作るもので、独立した要素を定義します。
例えば以下のような要素をさします。
- button
- text field
- menu
heading
命名規則は短く簡単に決めます。接頭語に
b-
をつけたりするケースもありますが、普通はつけません。- CSSセレクタは
class
属性だけで、tagやidを使わず他の依存性は一切無くします。
つまり
<div class="block"> ... </div>
そしてスタイルは以下のようにします。
.block { color: red; }
Element
Blockを構成するものです。
これは以下のようなものをさします。
- text fieldのlabel
- headingのlogo
- menuのitem
また、以下のようなルールがあります。
- 命名規則は
[BLOCK名__ELEMENT名]
で命名します。 - CSSセレクタは単独で指定します。つまり
[BLOCK名__ELEMENT名]
で指定します。
つまりHTML要素は以下のようにします。
<div class="block"> <span class="block__element">...</span> </div>
CSSは以下のコードにします。ここでは正しいコードと間違ったコードを示します。
✅良いコード
.block { ... } .block__element { ... }
❌悪いコード
.block { ... } .block.block__element { ... }
Modifier
ModifierはBlockやElementのバリエーションを定義するものです。
- 命名規則は
[BLOCK名orElement名]--[Modifier名]
でつけます - CSSセレクタは
.block--mod .block__element
でBlockレベルのModifierを定義し.block__element--mod
でElementのModifierを定義します
これを実践すると以下のような構成になります。
<div class "block block-mod"> <div class="blokc block--size-big> ... </div>
.block--hidden { ... } .block--mod .block__element { ... }
サンプル
例えば以下のようなフォームがあったとします。
<form class="form form--theme-xmas form--simple"> <input class="form__input" type="text" /> <input class="form__submit form__submit--disabled" type="submit" /> </form>
そして以下のようにCSSでスタイル付をしていきます。
.form { } .form--theme-xmas { } .form--simple { } .form__input { } .form__submit { } .form__submit--disabled { }
まとめ
これで圧倒的にCSS設計力がついたのではないでしょうか。
ぜひ、みなさんもBEMから実践してみてください。