CSS設計パターンガイド
「REMM」は、「BEM」のように使う、「OOCSS」のように使う、または混在させるなどの様々な設計パターンに対応できます。
これは、同じ記法でありながらも、サイトの特性(オフィシャル系、ポータル系、ブログ系、サービス系、管理画面系 etc.)に応じて設計を切り替えやすくなっていることを意味します。
反面、事前の方針決定がないまま分業すると、アプローチがバラバラになる可能性があります。
以下は、こういった状況を防ぐための使い方(CSS設計パターンやコーディングアプローチ)のガイドです。
A見出しやボタンなどの小さな部品をそのまま再利用する
見出しやリンクボタンなどの小さな部品を、そのまま再利用・配置して完成させるパターンです。
このアプローチが向いていると考えられるのは、システム管理画面、ドキュメントサイトやブログ、ポータル系、ニュースサイトなど、部品の形状やカラーや用法が統一されているようなサイト(言い換えるとシステマチックなサイト)です。
小さな部品の例としては以下のようなものです。
見出しテキスト
こういった部品は、粒度の大きな部品の内部に配置することが多くなります。
あらかじめ配置した親要素からの影響を受けにくいようにしたい場合は、以下のように内部の要素を「Element」化します。
見出しテキスト
上記のように記述していれば、他の大きな部品の中に配置したときに、その部品の内部に同じ.icon
や.text
といった名前を持つ部品があったとしても、命名が重複しないため影響を受けにくくなります。
バリエーションや変化版については、以下のような記述方法を採択できます。
見出しテキスト
上記の変化版・バリエーションの内部を、変化版に対して「Element」化すると以下のようになります。
見出しテキスト
同じ単語でも、マルチクラスで拡張したい場合は以下のようになります。
見出しテキスト
内部部品の.icon
をマルチクラスで拡張したい場合は以下のようになります。
見出しテキスト
他にもパターンは考えられますが、制作物の特性や種別に応じてアプローチを切り替えることができます。
B中粒度のブロック単位で仕上げる
ヘッダーやフッター、ナビゲーション、新着情報やコンテンツ表示など、制作者が定めた範囲の領域(ブロック)に名前を与え、その粒度で「Roots」による一元管理で影響を閉じるアプローチです。
以下のものは、「Roots」の名前と汎用的な単語のみで影響を閉じようとしたものです。
- …
- …
- …
…
…
…
上記のような場合でも、「予約語のように扱う」のアプローチをおこなった場合は、内部に何かしらの要素がネストされても問題は起こりにくいといえます。
命名によって影響をあらかじめ回避したい場合は、以下のように内部部品を「Element」化します。
- …
- …
- …
…
…
…
変化版、バリエーションが必要なときには、以下のように拡張できます。
…
…
…
…
…
…
…
…
…
…
…
…
コンテンツ表示など、内部に様々な部品が必要な場合
部品として区切った範囲が「コンテンツの1ブロック分」などの場合、内部の構成部品は多種多様となります。
たとえば、専用の見出し、リスト、画像、テーブル、テキスト、それらを分離してまとめるための領域や構造体などです。
内部に配置するこういった部品すべてを「Roots」の専用部品として命名すると以下のようになります。
ギャラリーの見出しテキスト
見出し直下の簡単な説明文章
この状態であれば、変更が必要になったとしても、CSSの特定が容易になります。
(※Sassで&
記法によるセレクタ記述をしていない限り、修正や改修の際には、ブラウザのインスペクタ機能(要素検証)で要素名を調べる→エディタで全検索→ピンポイントでヒット といった使用感になるでしょう)
反面、たとえば.photo_gallery--title
(コンテンツの見出し部分)が、異なる他の部品の見出し部分と同一デザインであった場合、要素の名前が異なるために同じCSSプロパティを記述することになります。
この例においては、Sassの@mixin
や@extend
を使っていれば編集は容易になります。
レギュレーションによる禁止などで、@mixin
や@extend
を利用できない場合には、異なる部品の該当箇所すべて(※10個の部品があるなら、10箇所)に同じCSSプロパティを記述することになります。
内部に別の中粒度部品を設置する場合
.photo_gallery
の例で、.photo_gallery--body
の内部に「写真一覧」の部品を配置する場合を例に説明します。
このとき、採択できるのは大きく分けて2パターンです。
ひとつは、.photo_gallery
に所属する部品として作成する場合。
もうひとつは、別の中粒度部品として作成してネストする場合です。
結論から言えば、どちらでも問題はなく、どのように管理したいかで決められます。
以下は、写真一覧のブロックに異なる「Roots」の部品をネストしたパターンです。
~
-
-
-
~
上記の場合、.image_list
を別の場所でも再利用するためのこのようにしているのかもしれません。
以下は、.photo_gallery
の内部部品として設置した場合の例です。
~
-
-
-
~
写真を配置するためのコンテナを.photo_gallery--image_list
とし、さらにその内部は命名が冗長になるのを避けるために、「予約語のように扱う」のアプローチを採択しているような例です。
写真一覧はここでしか使わないと確定しているため、親の内部部品として影響を閉じているのかもしれません。
C中粒度で影響を閉じながらも、小さな部品を再利用する
「(B)中粒度のブロック単位で仕上げるアプローチ」と、「(A)小さな部品をそのまま再利用する」を併用したパターンです。
以下は、中粒度の部品に「Roots」の異なる小さな部品を配置しています。
中粒度の部品(.photo_gallery
)は、「(B)中粒度のブロック単位で仕上げる」アプローチで構造体のみをスタイリングし、
見出し(.heading
)やリンクボタン(.link_btn
)は、「(A)小さな部品の再利用」のアプローチでスタイリングします。
ギャラリーの見出しテキスト
見出し直下の簡単な説明文章
このとき、それぞれの「Roots」(photo_gallery
、heading
、link_btn
)において、それぞれが影響を閉じた状態でスタイリングできていれば、.photo_gallery
からの子孫セレクタで.heading
を指定してスタイリングする必要はありません。
しかし状況によっては、例外的に上書きしたい場合もあります。
そのときは、.photo_gallery
側のSassファイルに、その旨のコメントを添えて上書きのコードを記述すれば、大抵の制作者はその意図を理解できます。
こういった「Roots」から「Roots」への上書きを不許可にしているわけではありません。
多くの例外が発生するのであれば、設計そのものを見直す必要もありますが、部分的な上書きまで制限すると、状況によっては、一か所のデザイン再現のために全体を見直すという「不毛なゲーム」が始まってしまいます。
D小さな部品をイニシャライズし、差分スタイリングで完成させる
小さな部品に基礎のスタイリングのみをおこない、ネストした親からの子孫セレクタでデザインを完成させるアプローチです。
このアプローチが有用な状況とはどういうものかというと、
「小さな部品は一見そのまま再利用できそうだが、よく見ると場所ごとに若干デザインが異なる」といった場合です。
以下の例は、HTMLコードだけ見れば「(C)中粒度で影響を閉じながらも、小さな部品を再利用する」とほぼ同じですが、CSS側のアプローチを変えることで差分スタイリングをおこなおうとしているものです。
ギャラリーの見出しテキスト
見出し直下の簡単な説明文章
CSS側では、.heading
や.link_btn
とその内部部品に、全域で共通するスタイルのみを与えます。
(たとえば、フォントサイズやテーマカラー、余白などです)
そして、.photo_gallary
の子孫セレクタにより、.photo_gallary
で求められるデザインを完成させます。
同様に他の部品の内部でも、.photo_gallary
とは異なる、その部品が求める.heading
と.link_btn
のデザインを完成させます。
考え方としては、リセット系のCSSだけでは影響範囲が広くなりすぎるために、「独自の命名でスコープを狭めてイニシャライズしている」と捉えます。
これによって、意識は「複雑な上書きルール」から、「シンプル化するための部分的な初期設定」に変化します。
単語の汎用化|予約語のように扱う
「汎用的な短い文字列」を再利用しながら、影響を分離するための方法を紹介します。
「汎用的な短い文字列」とは、フロントエンド制作でよく利用する.icon
や.text
、.list
、.link
などの短い英単語です。
これらを要素への意味付けとして使用すると、いたるところで同じ単語を使用することとなり、自身より大きな粒度の部品にネストする機会も多くなります。
影響の分離を考えたときは、これらの小さな部品を「Roots--Element」の型で固有の名前を与えてもよいのですが、場合によっては「余計な手間」「命名が冗長になり見通しが悪くなる」ことがあります。
予約語による影響分離の基本となる考え方
CSSの仕様として、セレクタのみを記述してプロパティが空の状態でも画面上にエラーが表示されることはありません。
また、SassではCSSプロパティを記述しなければ、そもそもCSSとして出力されません。
この2つの特性を生かすと、制作で何度も使用するような単語には直接的なスタイリングは与えない。というアプローチをとることができます。
//Roots
.text {}
予約語運用のメリット
予約語のように扱うと決めた単語・文字列は、いたる所で再利用できます。
なぜなら、第一セレクタでCSSプロパティを指定しないと決めているため、グローバルな影響が出ないからです。
そして、ネストした他の大きな粒度の特性に応じて、様々な姿に変化できるようになります。
見出しテキスト
上記は、「Roots」内部に同じ単語(.icon
、.text
)を使用していますが、第一セレクタでスタイリングしていないため、グローバルな影響は出ません。
そして、それぞれの親要素(Roots)において求められるデザインに変化できます。
単語を追加していくことによる影響分離方法ではなく、いわば「引き算の影響分離方法」となります。
予約語の明示と情報共有
予約語のように使用していることを情報共有するには、その単語を「Roots」としたSassファイルを作成します。
そして、セレクタを記述し、中身は空の状態で置いておきます。
このとき、Sassコメントで用途を記述しておけば、情報共有するための「簡易の仕様書」や「辞書」になります。
//text
//--------------------------------
//意味付け用の「予約語」として使用し、グローバルなスタイルは与えない。
//Roots
.text {}
上記の状態であれば、「この単語はHTMLに記述されてはいるが、CSSとしてグローバルな影響は出ていない」ということが分かります。
予約語の拡張
予約語として扱っていたRootsの単語そのものを拡張し、辞書を増やすような場合は以下のようにセレクタを追記し、同じ要領で中身は空の状態で置いておきます。
// text
// --------------------------------
// 意味付け用の「予約語」として使用し、グローバルなスタイルは与えない。
// Roots
.text {}
// 成功系のテキスト
.text-success {}
// 失敗・エラー系のテキスト
.text-error {}
さらに、「(D)小さな部品をイニシャライズし、差分スタイリングで完成させる」といったアプローチに切り替える必要が出てきた場合、このtext.scss
の中身を書き換えることで、素早く用途を変更できます。
// text
// --------------------------------
// 差分スタイリング用
// グローバルな基本スタイルのみ指定する
// Roots
.text {
…
}
// 成功系のテキスト
.text-success {
color: green;
}
// 失敗・エラー系のテキスト
.text-error {
color: red;
}
REMM使用における影響分離の考え方
影響分離の考え方をガイドとして記載します。
CSS編集における「意図しない影響」や「望まないプロパティ汚染」のメジャーな例の一つとして、
「追加した要素に対する、親要素の子孫セレクタの影響」が挙げられます。
これを防ぐには、何らかの部品を作成しているときに以下の2点を意識します。
- A今後、この部品の内部に「他の部品が追加される可能性はあるか」、また「その可能性は高いか、低いか」
- B現在編集中の要素内に他の部品を追加した場合、「その内部部品となるものに影響を与えることはないか」
Aの検討事項について
「他の部品が追加される可能性はあるか」という検討事項に対し、可能性が「無い」、または「低い」と判定できるなら、子孫セレクタを使用してもリスクは低いといえます。
例としては、ネスト最下層にあるような小さな部品や、ブランディングエリアなどの、全域で使用される部品です。
具体的な例を挙げると、「見出し内部のアイコンとテキストが、その見出しの内部にもうワンセット追加される」というのは一般的には考えにくいため、サイトのロゴやサイト名を表示するエリアに全く用途の異なるものが追加される可能性は低いと判断できます。
Bの検討事項について
今後、「内部部品となるものに影響を与えることはないか」という検討事項に対しては、次の2つで回避できます。
- 1命名のユニーク度を高め、サイト内で一意にする
- 2プレーンなHTMLタグを子孫セレクタに使用しない
(1)の「命名のユニーク度を高め、サイト内で一意にする」に関しては、
命名が全域で一意であり、その命名を誤って使用してしまうことがなければ、どれだけCSSの詳細度が低くても他から影響を受ける可能性は低いと言えます。
(※このための方法として「Element」化があります)
(2)の「プレーンなHTMLタグを子孫セレクタに使用しない」に関しては、
そもそもHTMLは文書作成用の言語であるため、当然ですが同じ文字列(タグ)を重複利用します。
「プレーンなHTMLタグ」を子孫セレクタに含めていると、ネストが発生した場合、プロパティ汚染のリスクが高くなります。
小さな部品にも名前を与え、その名前をセレクタに使用することで、未然にリスクを回避できます。
(※このとき、細部部品の命名冗長化を防止するために「予約語」のアプローチがあります)
直接表示に関わるHTMLコードは、body
を最上としたネストの集合体です。
何かを追加しようとすれば、常に影響が発生し得るのは仕様上避けられず、更には、改善や顧客の要望などによって、将来どこに何が必要になるのか(追加されるのか)は分かりません。
これらの懸念は「プロジェクトによりけり」ではありますが、将来不確定なものに影響を与えたくない場合は、下方階層への影響を「今現在の段階で、そのスコープの中で閉じておく」というのが最善の策と言えます。
Rootsに対するプレフィックス(接頭辞)の考え方
「SMACSS」や「FLOCSS」などのCSS設計手法を導入している場合、以下のようなプレフィックスが必要になるかもしれません。
- l-header
- l_header
- m-header
- m_header
- c-header
- c_header
- u-clearfix
- u_clearfix
- is-active
- is_active
これらのプレフィックスの目的は、人が素早くその役割や意味を把握するためのものであるため、元の単語の一部として捉えることができます。(※このときの連結記号に特にルールや制限はありません。)
既存CSS設計手法のプレフィックスを導入
…
…
上記のように、様々なプレフィックスを付与できます。
独自の一時拡張用のプレフィックス
恒常的に付与するプレフィックスではなく、「Rootsに紐づく部分的な拡張」の場合は、同じSassファイルで管理できます。
以下は、「l-」に対して「意匠を与えないレイアウト専用のボックス」というルールを独自に与えてプレフィックス化した例です。
このとき、.l-side_menu
は、「Roots」の.side_menu
を、特定条件でposition:fixed
にするためだけに働いているかもしれません。
このような場合、.l-side_menu
は、.side_menu
に帰属していると予測がつくため、わざわざ別のSassファイルで管理する必要はなく、.side_menu
のSassファイルで管理できます。
このように、何らかの独自規定のプレフィックスがあったとしても、その「Roots」に紐づいていると予測可能であるなら、別のSassファイルとする必要はなく、同じSassファイルで管理します。
CSSフレームワークのプレフィックス
CSSフレームワークの中には、特定のプレフィックスを付与するものがありますが、これらも同様の考え方で利用できます。
以下は、「UIkit」のプレフィックスを持つカードです。
…
「Roots」であるcard
という語句に、識別子としてuk-
のプレフィックスが付与されていると捉えることができます。
CSSフレームワークの併用
パーツの命名とデザインがセットになって部品提供されているような「CSSフレームワーク」を併用するパターンです。
前提事項として「併用」がある状況とは、「使用しているCSSフレームワークの部品だけではプロジェクトの要件を満たせないために、何らかのものを追加作成しなければならない」といった状況であると考えられます。
「CSSフレームワーク」の大半はケバブケースを採用しており、REMMも同様です。
このため「記法違いでコードの見通しが悪化する」というリスクは低いと言えます。
Bootstrapを例とした併用アイデア
Bootstrapを例に挙げると、まず、Bootstrapを「MCSS」の概念を拝借して基底に敷きます。(※BaseなどのSassディレクトリ)
これによって、Bootstrapを使用しながら、他のディレクトリで独自部品を管理・追加しやすくなります。
命名規則のコンフリクトが起こるケース
命名規則や概念のコンフリクト(衝突)が起こるのは、
たとえば、Bootstrapの「Components」として定義されている.card
の専用部品などです。
Bootstrapでは.card
専用の見出しは.card-title
ですが、「REMM」で記述すれば.card--title
となります。
この例では、.card-title
を使えばBootstrapのスタイルが適用されますが、「内部部品に独自定義のものが必要なら.card--title
で拡張する」といった使い方ができます。
当然のことながら、.card
という同名の「Roots」を作成すれば干渉しますが、そもそも、CSSフレームワークを意図的に導入している場合は、このコンフリクトはどんな状況でも起こります。
他のCSS設計手法との併用
厳格な命名規則が存在するものは、記法の違いでコンフリクトしますが、それ以外のものであれば、概念や手法の良い所を組み合わせやすいと言えます。