CSS設計考察 前編

CSS設計考察 前編

去年8月末の更新を最後に投稿が途絶えてしまいました。死んでないです、生きてます。とりあえずWordpressの編集画面がGutenbergになってクッソ使いづらいなあなどと感じております。

久々の投稿がこんな手垢でゴムボール作れそうなほど基本的な内容のネタで申し訳ないですが、独自の視点も混ぜて書いてあるのでそれなりに有用だと信じたい。真面目に勉強したい人は書籍とかもあるのでそういうのを読むのも悪くないでしょう。

本記事では、BEMとかOOCSSとかSMACSSとかFLOCSSとか色々CSSコンポーネントがある中で、もっといい方法はないかと自分なりの最適解を模索をしていく内容になります。

基礎知識

では早速・・・・と言いたいところですが、今回の記事では本題に入りません。今回は技術的なテクニックというよりは読み物テイストな内容になるので、一応初学者向けの解説も挟んでいこうかと思いまして。でもさすがに0から話し出すとキリがないので、HTMLとCSSがなんとなく分かるぐらいの認識レベル想定で書かせてください。分かんなかったら知ったかぶりして読んでください。

前提:用語解説

固有名詞が結構頻出するので先んじてピックアップしておきます。読んでて分かんない言葉が出てきたら遡って確認できるように書いておきますが、「このぐらい分かってる」って人は読み飛ばしていいです。あくまでざっくりした説明なので詳しく知りたかったらGoogle先生に聞いてください。

ルールセット/セレクタ/宣言ブロック/宣言/プロパティ/値

図による例

分かりやすいように該当範囲を青背景で示します。

ルールセット:
セレクタ・宣言ブロックから成る構成
p { color: #000; } ←全体をさす

セレクタ:
スタイルを適用する対象。セレクタが要素、たとえば<div>や<p>、<span>などの場合は要素セレクタ(またはタイプセレクタ)と呼び、セレクタがクラスだった場合はクラスセレクタと呼ぶ。
p { color: #000; }

宣言ブロック:
中括弧で括られた部分全体をさす。セレクタに適用されるスタイル全体
p { color: #000; } ←波括弧の中身全体

宣言:
宣言ブロック内の、プロパティ: 値;から成る各行。
p { color: #000; } ←波括弧内の各行

プロパティ:
適用するスタイルの名前
p { color: #000; }

値:
適用するスタイルの内容
p { color: #000; }

ちなみに波括弧のことをブレースと呼びます。
参考:セレクタ(selector)|W3G

シングルクラス/マルチクラス

シングルクラス:
1つの要素に1つ(ないし2つ程度)のクラスを指定すること。なるべく単一または少量のクラスでCSSを設計しようという思想が根源

マルチクラス:
1つの要素に複数のクラスを指定すること。複数のクラス指定を前提としてCSSを設計しようという思想が根源

なぜ異なる二つの思想が存在するのかは後で説明します。

アンチパターン祭り ~なぜCSS設計が必要か

とりあえず下記のHTML・CSS群を見てください。

例1:詳細度バトルはとてもつらい

詳細度の話はかなり長くなってしまうので端折ります。分からなければググるなどしてください。
おすすめリンク:第1回「詳細度」|100年後も崩れないCSS勉強会

<div id=”sample”><p class=”test” style=”color: red;”>詳細度最強決定戦</p></div>
body { color: black; }
#sample { color: white; }
#sample .test { color: blue; }
.test { color: yellow !important; }
* .test { color: green !important; }
#sample p.test { color: purple !important; }

testが最終的に何色になるか一目で理解できますか?(一番下の記述が効いて紫色になります)。これは極端な例ですが、「自分の書いた宣言を優先させたいがために他のルールセットを蔑ろにするようなセレクタ指定」は後々になって禍根を残す傾向が強いです。セレクタを子々孫々繋げたり、!important宣言で強権行使したり、あるいはその逆に新しくルールセットを書く手間を惜しんで影響範囲の広い/予測困難なセレクタに対して汎用性の低そうな宣言を記述したり、などといった邪悪な行いは最大限避けるべきでしょう。あと、CSSの存在意義を否定する蛮行なのでタグにスタイルを直書きしちゃダメです。

例2:その場しのぎのコピペ量産は将来的な破局を招く

.container01と.container02の違いはbackground-colorだけにも関わらず、スタイルの記述が結構複雑になってしまいました。まだこれだけで済んでいるからいいものの、.container03, .container04…とパターン違いが雨後の筍のように増え続けていく可能性があるとしたらゾッとしますね。

上記のような設計をしていれば、背景色の異なる.containerの数が増えたとしても
.container.color03 {…}
といったルールセットを追記するだけで済みます。

例3:セマンティクスを意識しないクラス命名が牙を剥く瞬間

セマンティクスとは何か・・・も、観念的な部分が多いのでググってください。(ひょっとしたらいつか掘り下げて記事化するかもしれませんが)
おすすめリンク:
セマンティック HTML とはなんじゃろう
[HTML]セマンティックで正しいコードを書こうとする理由 ― 二人の主を戴く技術者

HTMLの場合、「段落」として解釈されるpタグや「見出し」として解釈されるh1タグなどそれらの要素が持つ「意味」まで含めて検索エンジンに解釈されています。一方で、それらのタグに付与されたクラス名が持つ「意味」まで検索エンジンが理解することはできません(現状は)。「じゃあクラスの命名規則なんて考えず適当でいいよね!」って?…本当にそうかな。

#kfoajdr234 .abjofg43tee_r3 {…}

こんな名前のID・クラスがHTML上のどこに存在していてどういった働きをするか、果たして予測できるだろうか(反語)。検索エンジンはおろか、書き手である人間にさえ理解不能な命名で徒にメンテナンス性を下げる意味はないですよね。「分かった!意味があればいいんだな!」ですって?

.orange { color: orange; }

ええと・・・これはダメです。「ナンデ!?会社のブランドカラーであるオレンジ色にしたいから.orangeってクラス名にしたのに」と思われるかもしれませんが、じゃあ後々になってブランドカラーがピンクになったとしましょう。

.orange { color: pink; }

この記述は明らかにムジュンしています。「なら、.pinkのスタイルを新しく書いてHTML上のorangeクラスを書き換えれば・・・」それ、orangeクラスの使用箇所が各ページに点在していて計1000個あったとして、grep置換とかも使えなかったらどうします?意味上の矛盾による混乱を承知で上記のような対応をするしかないですよね。

.brandcolor { color: orange; }

最初から上記のようにしておけば、つまり、「見た目」ではなくそのクラスが持つ「意味・意図」を考えて命名しておけば後から色が変わろうが形が変わろうが「ああこのルールセットはHTML上のここで使われてる記述なんだな」と理解しやすくなるのです。

CSSコンポーネント設計の紹介

CSSの設計を考えることの重要性はご理解いただけましたでしょうか。WEBページというのは内容の更新・変更・修正・追加・複製・削除・破壊といった作業が行われる前提で出来ているものなのです。ゆえに、将来を見越した予測しやすく、再利用しやすく、保守しやすく、拡張しやすいCSS設計が大事だよ、って海外では言われてます。こうしたCSSにまつわる設計思想をCSSコンポーネント設計とか呼んだりもします。コンポーネントの意味が気になるかもしれませんが日本人はみんなフワッとしたニュアンスで使ってるので気にしなくていいです(えぇ・・・

一応補足:
コンポーネントとは、直訳すると「部品」という意味の単語です。これはソフトウェア工学などで使われる言葉で、前提に関心の分離という考えがあります。これは、プログラムを特定の機能・振る舞いといった関心(≒目的)ごとに分離するというものです。CSSコンポーネント設計においては、「これはボタンという”構造”のために付与されたクラス」「これはボタンの色を変更するための装飾、すなわち”見た目”のためのクラス」といったように、目的や意図に応じてクラスの命名や使い方を切り分ける(構造と見た目の分離)、というような思想があります。

OOCSS

Object-Oriented CSSの略。オブジェクト指向SSSを意味します。オーオーシーエスエスって読むのが一般的じゃないですかね多分。かの有名なBootstrapでも採用されている設計でもあります。

二つの原則

OOCSSには二つの原則があります。

構造と見た目の分離
コンポーネントの基本構造と、具体的なルール・機能を分離すべしという原則です(さっき補足でフライング解説してしまいましたが…)。さっきのアンチパターン例2のような考え方でクラスを命名していきます。下の例だと、.alertが構造で.success、.warning、.errorが見た目に該当します。

<a class=”alert alert-success”>成功時アラート</a>
<a class=”alert alert-warning”>警告時アラート</a>
<a class=”alert alert-error”>エラー時アラート</a>
/* 構造 */
.alert { border: 1px solid #333; }
/* 見た目 */
.alert-success { border-color: #494; }
.alert-warning { border-color: #DD0; }
.alert-error { border-color: #C44; }

手法は様々ですが、他のルールセットでも

コンテナとコンテンツの分離
場所に依存しないクラスを書くべし、という原則です。下の例でいうと、#headerや#footerがコンテナで、.logoがコンテンツです。

<div id=”header”><div class=”logo”>ロゴ</div></div>
(略)
<div id=”footer”><div class=”logo”>ロゴ</div></div>

.logoが#headerにあろうが#footerにあろうが同じ部品が表示されるようにせんかい、っていう考えです。もし「#footer内ではロゴを小さくしたい」という場合はフッター内の当該箇所を<div class=”logo small-logo”>というような形にして対応します。

SMACSS

Scalable and Modular Architecture for CSSの略ですが略称以外で呼ばれることはないので忘れてもいいです。pronounced “smacks”って書いてあったので読み方はスマックスです。「ルールセットの増大に伴って『どのスタイルがどこに当たるのか分からない』など管理上の問題がでてきた」「場所に依存しないクラスを意識するあまり、似たような英単語のクラスが増えてこんがらがってきた」といったOOCSSの欠点を補う、より発展的な考え方です。

ルールセットの区分

SMACSSではCSSのルールを5種類に区分しました。

Base
読み方:ベース。日本語的には「下地」といったニュアンス
接頭辞例:なし(IDやクラスは使わない)
要素そのもののデフォルトスタイル(bodyやaタグの基本色、各種Reset/Normalizeなど)。影響範囲が広いので基本的にpaddingやmargin、widthといった幅や余白に関する指定は避けるべき

Layout
読み方:レイアウト
接頭辞例:l-*, layout-*
ヘッダー・フッター、サイドバーやグリッドなど、ページ構成の大枠となるルール。あくまでページ内の領域を定めるためのルールなので、ヘッダー内のロゴやサイドバー内のバナーなど、具体的なパーツやコンテンツはLayoutではなく下記のModuleに区分される
例えるならば、Layoutが部屋の間取りでModuleは家具、といったところか

Module
読み方:モジュール。ざっくり言うとページを構成するための「部品(パーツ)」
接頭辞例:省略またはm-*
ボタン、ロゴ、見出し、テーブルといった、(共通利用されうる)ほぼ全てのルールが該当する

State
読み方:ステート。「状態」
接頭辞例:is-*
JSでの制御など、特定の状態で働くルール
例:要素を一時的に隠すための.is-hiddenといったクラスなど。「タブがアクティブ時に付与されるクラスで、その状態の時は見た目が変わる」というような、前者の例と違ってあらゆるパターンで利用できるとは限らない汎用性の低いものに関しては、.is-tab-activeというようにModule名を含めた命名することによって競合を回避する(.tabがModuleに該当)。

Theme
読み方:テーマ
接頭辞例:t-*, theme-*
Stateのような小さい単位での状態範囲とは異なる、サイト全体の見た目などを制御するためのルール
YouTubeTwitter公式アプリのダークモード(夜間モード)、Gmailのテーマ変更Yahoo! Japanの右上にある四角ボタンを押した時の挙動などをイメージしてもらえればよい

BEM

Block, Element, Modifierの略でBEM(ベム)です。早く人間にどうたらみたいなギャグはサムイんで言いませんよ。マルチクラスのOOCSSと異なり、シングルクラスでの設計となります。海の向こうでは一番普及している設計思想なようです(※2017年時点での話なので今は違うかも)。

マルチクラスには、HTML上でのクラスに関する記述が簡素、ルールセットの再利用性が高いといったメリットがあるが、一方で、ルールセット間に暗黙の依存関係が生まれることなどを問題視する声もある。対するシングルクラスはユニークかつセマンティックな(≒一義的かつ明示的な)クラスの命名を行うため、詳細度の違いによる振る舞いの差異や記述の打ち消しといった問題に悩まされずに済むという点で優れています。代償としてクラス名は冗長になり、キモくなります。

関連リンク:BEM派かOOCSS派か、チェックリストを作ってみた

MindBEMding;BEMの命名規則

BEMの命名規則には3種類の区分があります。

Block
読み方:ブロック。「ひとかたまり」というようなニュアンス
表記例:.block
ルールセットの基本となる独立した単位。SMACSSにおける「Module」(部品)に似た概念といえる。以下に続く、Element・ModifierはこのBlockを基点として命名する

Element
読み方:エレメント。ブロックを構成するための「要素」
表記例:.block__element。孫要素は.block__element__element
Blockの構成単位。Blockが「親」だとしたら「子」に該当する概念
例:リスト全体がBlockだとしたらリストの各項目がElementにあたる

Modifier
読み方:モディファイアー。直訳すると「修飾語句」。色やサイズなど差異を意味するもの
表記例:.block–modifier/.block__element–modifier
BlockまたはElementのバリエーション違いの要素を意味する。Elementと違ってクラス名の区切りが_(アンダースコア)ではなく-(ハイフン)なのは、親子関係のない「対等」な関係だから
OOCSSの考え方でいうと、BlockやElementが「構造」に該当し、Modifierは「見た目」にあたる。また、記法は異なる場合があるが、このModifierという考え方の概念自体は後述のコンポーネントにも受け継がれている

BEMはSassやStylusといったCSSメタ言語と相性が良いです。というより、CSSメタ言語を使用することを前提とした考え方と言っても差し支えないでしょう(じかにCSS書くのはわりと苦行です)。

MCSS

Multilayer CSSの略で、BEMとOOCSSの原理を基にしたCSS設計です(そのため、BEMライクの記法で書かれていることも多いです)。CSSの記述郡をレイヤー(層)に分けて管理しようという考え方が特徴です。

MCSSのレイヤー区分

MCSSのレイヤーは3層(+下地)に分かれています。再利用を前提とした汎用性の高い低層レイヤーを、固有性・独立性の高い高層レイヤーのルールセットで上塗りしていくようなイメージです。

ゼロレイヤー(下地)
名称:Foundation(ファンデーション)
reset.cssやnormalize.cssなど。SMACSSにおけるBaseに似ているが、MCSSにもBaseという名前の区分があるので少しややこしい(後述)

ファーストレイヤー(第1層)
名称:Base(ベース)
再利用可能な標準スタイル。ボタンやナビなどの各ページで共通利用されるパーツのため、抽象的なクラス命名・ルールセットにする必要がある。SMACSSにおけるModuleに近い

セカンドレイヤー(第2層)
名称:Project(プロジェクト)
ページを構成するための部品。特定のページ/ページ郡では利用されるが、そうでないページでは利用されないこともあるようなルールセット。Baseとは異なりユニークなクラス名にすることで、一目でどのページで使われているスタイルか識別できるようにする

サードレイヤー(第3層)
名称:Cosmetic(コスメティック)
色やサイズ変更など、わずかに影響するスタイル。.font-size_XL、.margin-t_Lのように、目的が明示的な命名にする

例外
名称:Context(コンテクスト)
レイヤーという概念の影響を受けない、特殊な立ち位置のルールセット。IE,Edgeなど特定ブラウザ時のみ有効にしたいスタイルや、ログイン時のみ有効となるスタイル、メディアクエリなど。特定条件下でのみ働くという点で、扱いとしてはSMACSSにおけるStateに近い

FLOCSS

名前の由来はFoundation, Layout, Objectの頭文字を取ったものです。発音はSMACSSがスマックスならフロックスじゃないかな(知らんけど…)。あくまで個人的な肌感ですが、国内では現状この手法を取るのが一番メジャーだと思います。

FLOCSSのレイヤー区分

FLOCSSは、簡単に言うとMCSSとSMACSSを混ぜたような考え方といえるかもしれません。Foundation, Layout, Objectという3つのレイヤー区分があり、また、Object内には3種の子レイヤーが存在します。

Foundation
読み方:ファンデーション
SMACSSにおけるBase、MCSSにおけるFoundationに等しい。resetやnormalize、ページ全体の背景や基本的なタイポグラフィなどが該当する

Layout
読み方:レイアウト
ヘッダーやフッターなど、ページを構成するための基本となるスタイル。SMACSSにおけるLayoutとほぼ同じ
ただし、SMACSSとは異なり、グリッドのような汎用的に使われるコンポーネントはObjectに分類される。基本的にページ単位で唯一の存在である要素がここに区分されるという扱いなようだ

Object
読み方:オブジェクト
Component・Project・Utility三種類の区分があります。

Component(コンポーネント):ボタンやテーブルのほか、ページ内ナビゲーションやグリッドなど、汎用性が高く、再利用できるパターンのルールセット。できる限り最低限の機能を持ったものが望ましい。MCSSにおけるBase・SMACSSにおけるModuleに近い概念
接頭辞例:c-*

Project(プロジェクト):MCSSにおけるProjectに等しい。記事一覧や画像ギャラリーなど、特定のページでは利用するルールセット
接頭辞例:p-*

Utility(ユーティリティ):ComponentやProjectレイヤーのModifierで解決することが難しい・適切ではない僅かなスタイル調整のためのクラスなどが分類される。MCSSにおけるCosmeticに似ている
接頭辞例:u-*

おわりに

いきなり学習塾もビックリの詰め込み教育な内容になりましたが、ここまではジャブです。こうした知識を活かしてどうやって自分や自分のチームに最適なコーディング規約を策定するか、というのが本筋なわけで…。とはいえ、さすがに情報量が多すぎるので今回の記事はこれでおしまいです。今回の記事を一言でまとめると、CSSはグチャグチャになりがちなので、様々な考え方でルールセットを整理しようとしたというお話でした。後編では辿り着いた自分なりの答え(に近いもの)も提示しようかなと。果たして後編が投稿されるのはいつになるのか!?乞うご期待