CSS|CodingRule
CSS を体系的・効率的に書くためのメソッド
はじめに
CSSの設計には、OOCSS, BEM, SMACSS, FLOCSS といったメソッド(手法)が存在します。チームで仕事をする際には(ひとりプロジェクトでも)、CSS が一定のルールに基づいて設計されていることが重要で、これが作業の効率化やメンテナンス性の良さに直結します。一般に以下の4点に配慮した設計が必要であると言われます。
- 予測しやすいこと
- 再利用しやすいこと
- 保守しやすいこと
- 拡張しやすいこと
CSS が抱える問題
CSS の書式は、セレクタ(HTML上のターゲット)に対して、実現したいスタイルを羅列するだけ・・というシンプルな仕組みなので、他の言語に比べると使うこと自体は簡単なのですが、大規模なサイトのデザインでコードの行数が増えてくると、とたんに混乱が生じます。ここではまず、CSSのコーディングが抱える問題をタイプ別に確認します。
多重定義の問題
CSS のコーディングでは、同じセレクタに対して多重にスタイルを定義することができます。逆にいうと多重に定義してもエラーとしてはじかれることがないので問題点を発見しにくい・・という問題を孕みます。
.sampleArea { background-color: black; } : .sampleArea { background-color: red; }
この性質は、コードの末尾に「追加のCSSを加えて調整する(上書きする)」といったかたちで積極的に利用される場合もありますが、以下のような「予期せぬ衝突」が起きるという問題を孕んでいます。
- 複数の CSSファイルを読んだ場合に、セレクタ名が衝突してしまう。
- コードの行数が多くなると、視覚的に離れた位置で多重に定義してしまう。
セレクタの優先度の問題
CSS には優先度(詳細度)の概念があり、これを理解していないと「なぜ効かないの?」と悩むことになります。以下の例では「クラス よりも ID の方が詳細度が高い」というルールによって、文字は赤で表示されます。
- CSS
#pageTitle { ・・・・ color: red; ・・・・} .textBlue { color: blue; }
- HTML
<h1 id="pageTitle" class="textBlue"> Hello CSS! </h1>
Hello CSS!
優先度は、コード上の順序、CSSが書かれた場所、重要度、詳細度に分けて考えることができます。以下、優先順位の高い順に紹介します。
- コード上の順序
CSSは上から下へと解釈され、後に書かれた宣言(ソースコードの下の方)が優先されます(後着優先)。
- CSSが書かれた場所
HTMLタグに直接書き込まれたインラインスタイルは、内部CSS、外部CSSよりも優先度が高く、常に他のスタイルを上書きします。内部CSS(<head>内に<style>タグを用いて書かれたCSS)と外部CSS(<head>内で読み込む外部ファイルに書かれたCSS)では優先度に違いはありませんが、後から読む方が先に読み込んだものを上書きします(降着優先)。
- 重要度 !important 宣言
この宣言があるとスタイルは最優先されてその他の宣言を上書きします。しかし !important は自然なカスケードを破壊するのでデバッグを困難にします。時間がなくて強引な解決が必要な場合にやむを得ず使う・・という場合を除いて、基本的には使わないのが理想です。
- セレクタが持つ詳細度
詳細度とは CSS 宣言が適用される際の重みで、セレクターの種類によって特定されます。以下、詳細度の大きな順に紹介します。尚、複数の宣言が同じ詳細度であれば、最後に宣言されたものが優先されます。- ID( #sample )
- CLASS( .sample )、属性( [ type="radio" ] )、疑似クラス( :hover )
- 型セレクター( h1 )と 疑似要素 ( ::before )
- ユニバーサルセレクタ( * )
- 参考:3桁の数字で表す詳細度
以下に事例を紹介します。3桁表現ですが、桁の繰り上がりはありません。また、桁の重みで計算するものでもなく、バージョン番号に近い概念です。- ID:1,1,1
- CLASS, 属性, 擬似クラス :0,1,0
- 型セレクター, 擬似要素:0,0,1
- ユニバーサルセレクタ:0,0,0
- 参考:詳細度の計算例
- nav#siteNav ul li:hover:1,1,3
- #siteNav ul li:1,0,2
- li.subMenu a:0,1,2
- .subMenu a:0,1,1
- h2:0,0,1
- 詳細度の計算ができるサイト:http://specificity.keegan.st/
- その他
- 直接対象となった要素は、祖先から継承した規則よりも常に優先
- 文書ツリー内での要素の近接性は、詳細度には影響を与えない
- ユニバーサルセレクター ( * )、 結合子 ( + , > , ~ , ' ' )及び否定疑似クラス ( :not() ) は詳細度に影響を与えない
スタイルが子要素にまで継承する問題
CSSスタイルは HTML上での子要素(DOMツリーの下位)にまで「継承」されるため、上位の要素に必要以上に詳細なスタイルを適用すると、下位の要素で上書きが必要になり、管理が煩雑になってしまいます。
- CSS
body{ font-size: 24px; }
- HTML
<section> <p>この段落の文字も24pxになる</p> </section>
スタイルが複合的に適用される問題
HTML要素はスタイルを複合的に受け入れます。 スタイルが合成された場合、結果を予測することが困難になるので、各スタイルは事前に厳密に設計する必要があります。以下、スタイル合成の例です。
- CSS
.class-01 { font-size: 16px; } .class-02 { font-color: blue; } .class-03 { font-weight: bold; }
- HTML
<p class="class-01 class-02 class-03">Sample Text</p>
Sample Text
一般的なコーディング指針
ID、クラスの命名規則について
- 当然ですが、日本語NG、全角NG、スペースNGです。
- 必ずアルファベットから開始する。
数字ではじまるID・クラス名は、認識されません。 - 原則小文字で単語の接続はハイフン(チェインケース)というのが一般的
参考
- ID名とクラス名を区別するために、以下のように書くケースもあるようです。
- ID名はキャメルケース > #camelCase
- クラス名はチェインケース > .chain-case
- 単なる単語接続はキャメルケース、構造上の親子をハイフン接続というのもわかりやすい方法です。
- siteNavigator
- siteNavigator-firstItem
参考:使う言語によって単語接続ルール(文化)が違います。
- CSS:チェインケース header-title
- JavaScript:キャメルケース headerTitle
- Ruby:スネークケース header_title
参考
- キャメルケースとスネークケースはダブルクリックで全体が選択できます
- ハイフンはダブルクリックでは選択できないので、コピペの際はドラッグする必要があります。
要素タグの利用は必要最小に
要素名に対して直接CSSを書くと、各所でそれを打ち消す記述が必要になってコードが肥大化するので、全体に統一的に適用する「リセットCSS」のようなケースにのみ利用するのがよいでしょう。
IDの利用は必要最小に
IDセレクタの利用は以下のような理由で、できるだけ使わないのが理想です。
- ID はページ内に1つしか使用出来ないという前提があるため、CSSのモジュール化に向かない、つまり再利用性が下がる
- ID は「詳細度」が高いため、追加のCSS等での上書きが難しくなる
逆に言えば、IDの利用は、以下のような場合に限った方がいいでしょう。
- 大枠のレイアウトブロック要素に使う
- 各ページを識別するための名前空間として使う
- ページ内リンクの飛び先として書く
クラスの定義・命名について
CSSフレームワークの設計を見れば明らかですが、クラスの定義は特定のHTML構造とは独立になされるのが理想です。「HTMLにCSSを適用する」という発想ではなく、「CSSをHTML要素に適用する」という発想で書くことを前提に定義するとよいでしょう。
- クラスは、役割・意味・構造を表すセマンティックなものにする
- .page-header、.site-navigator、.strong など、要素の役割を表す
- .text-italic、.text-bold など、見た目を表すクラス名はNG
- chain-case で接続する(CSSのプロパティーと同じ)
element-color menu-button menu-list
- HTML構造に依存しないよう定義する
例えば以下のような書き方は、HTMLの構造変更にあわせてCSSの変更が必要になります。クラス名のみで対象が特定できるよう定義するのが理想です。
.site-nav >div > div > ul
SMACSSの発想
CSSの設計手法として、、OOCSS, BEM, SMACSS, FLOCSS などがありますが、ここでは、SMACSS(Scalable and Modular Architecture for CSS:スマックス)を紹介します(好みの問題です)。
SMACSSは CSS を BASE, LAYOUT, MODULE, STATE, THEME に分類して記述する手法です。
ルール | 内容 |
BASE | 要素そのもののデフォルトスタイルを定義 |
LAYOUT | ページをエリアごとに分割するためのスタイルを定義 |
MODULE | 再利用可能な単位でパーツの見た目を定義 |
STATE | レイアウトやモジュールの特定の状態におけるスタイルを定義 |
THEME | サイトのルック&フィールを定義 |
ベース
サイト全体について、要素のデフォルトスタイルを定義します。主に以下のHTMLセレクタへのスタイル適用となります(IDやクラスは対象外)。
- 要素セレクタ( body, a )
- 属性セレクタ( input [ type=text ] )
- 擬似クラスセレクタ( a : hover )
bodyの背景や、リンクのデフォルトカラーや下線、フォントのスタイルなど、サイト全体で統一すべき要素のビジュアルを定義します。ここで書き込みすぎると、個々のパーツで「上書き」が増えるので、必要最小限にとどめるのがコツです。典型的なのは、以下のような CSS リセットです。
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, img pre, code, form, fieldset, legend, input, textarea,p, blockquote, th, td { margin: 0; padding: 0; } table { border-collapse: collapse; border-spacing: 0; } :
参考:リセットCSS
- Normalize.css:http://necolas.github.io/normalize.css/
- reset.css:https://yuilibrary.com/yui/docs/cssreset/
レイアウト
レイアウトルールでは、ヘッダー・フッター・ナビゲーション・メインコンテンツ・サイドバーなど、大枠のレイアウト(ページのエリア分割)に対してスタイルを適用します。詳細度の高い ID はなるべく使用せず、プレフィックス付きのクラスで定義して、後の モジュールやステートと区別します。
.layout_header { margin-bottom: 20px; } .layout_main { width: 70%; } .layout_aside { width: 30%; } .layout_footer { border-top: 1px solid gray; }
一般にヘッダやフッタなどの主幹レイアウト要素は「ページ内に唯一」なので、慣習的にIDセレクタが使われることが多いのですが、JavaScript のフックとして必要になる場合や、ページ内アンカーとして必要な場合を除いて、極力 ID は使わず、クラスで作ることが推奨されています。
モジュール
モジュールルールでは、再利用可能な単位でパーツの具体的な見た目の定義をします。ロゴ、ナビゲーション、タブなど「部品」の単位です。レイアウトと同様、クラス名にはプレフィックス module- をつけます。
- HTML
<div class="module"> <h1 class="module-title">TITLE</h1> <p class="module-description">Description・・・</p> </div>
- CSS
.module{ background: #EEE; } .module-title{ font-size: 18px; font-weight: bold; } .module-description{ font-size: 12px; }
- 子モジュールの名前は、親モジュールの名前をプレフィックスでつける。
- ただし、下層のタグ( p、a、li、など)は階層が変わる可能性もあるので、クラス名をつけずに子孫セレクタの形で指定する( > を使用)。
- モジュールは独立性の高いものとなるよう、親要素のレイアウトルールに左右されないようにする。
- レイアウトクラスの子孫に指定するのはレイアウトクラスのみ
- モジュールクラスの子孫に指定するのはモジュールクラスのみ
ステート
ステートルールは、モジュールの状態によってスタイルを上書きして見た目の変更を行います。プレフィックスには .is-hidden .is-active .is-current など is- を使います。
- HTML
<div class="module is-hidden”> <h1 class="module-title">TITLE</h1> <p class="module-description">Description・・・</p> </div>
- CSS
.is-hidden{ display: none; } .is-active{ display:block; }
テーマ
テーマルールは、サイト全体のテーマ統一、変更のための定義をします。例えば 「付加のCSS」を書く、あるいは、main.css を読み込んだ後、thema.css を読み込むなど、後からスタイルを変更させる場合に使います。
- main.css
.box { border: 1px solid; }
- theme.css
.box { background-color: #eee; border-color: #ccc; }
コーディングルールには様々あって、これが標準と呼べるものはありませんし、また「IDセレクタの是非問題」にも様々な意見があるようです。
いずれにしても、サスティナブルなCSSにするには、それなりにしっかりした「情報デザイン」が必要になるということ、また組織で制作にあたる場合は、メンバー間でルールをきちんと共有することが重要です。
BEMの発想
BEMはBlock Element Modifierの略です。
- Block:大枠となる独立要素
- Element:Blockを構成する要素
- Modifier:BlockやElementをスタイリングするもの
Block
いくつかのエレメントで構成された要素。これはページのどこに配置しても機能する独立した要素で、例えば、ヘッダー、フッター、ナビゲーション、コンテンツなどがそれにあたります。
Element
Blockの内部に置かれた個々の要素。例えば、タイトル、テキスト・画像、リスト、テーブルなどがそれにあたります。
Modifier
Block、Elememt の状態やスタイリングを設定するものです。例えば「メニュー要素のアクティブな状態」などがそれにあたります。
BEMの命名ルール
- 単語の区切りはハイフンではなくキャメルケースを用いる。
- アンダーバーは1つの構造、もしくは要素の親子関係を表すために用いる。
- ハイフンは構造、もしくは要素の派生を表すために使用する。