HTML CSS

CSSアーキテクチャRSCSSの規格解説

RSCSSの概要

RSCSS(Reasonable System for CSS)は、厳選した覚えやすいルールセットのCSSアーキテクチャですBEMのコンポーネントを参考にComponents、Elements、Variantsの分類が用意されていますが、BEMのとっつきにくいクラス名のルールは使っていません。

クラス名はマルチクラス前提で、クラス名が長くなるのを避けるためにCSSのネストと子セレクタ(>)の利用を推奨しています

RSCSSの公式ドキュメントにて自身のルールをアイデアセットと紹介しているだけあって、規則はミニマムです。規定されていない部分を補う必要がありますが、覚えやすく、導入コストが低いのが最大の魅力です。

Components

ヘッダ、ナビゲーション、ボタン、カードなど、UIの各パーツはコンポーネント(構成要素)に分類されます

コンポーネントはどこにでも置くことができ、コンポーネント内にコンポーネントを配置することもできます。

コンポーネントの命名

コンポーネントは、ハイフンで連結された最低2つの単語で命名されます。ハイフンで連結されたクラスは、必ずコンポーネントです。

コンポーネントの例:

  • いいねボタン(.like-button)
  • 検索フォーム(.search-form)
  • ニュース記事カード(.article-card)
  • 名前空間付きコンポーネント(.rico-custom-header)

Elements

エレメント(要素)はコンポーネントを構成する子要素ですエレメントは自分が属するコンポーネント内でのみ利用可能で、独立して使われることはありません

検索フォームのコンポーネントに内包されるテキストボックスやボタンがエレメントです。また、上の図の.tabのように、コンポーネント内で何度でも繰り返し利用できます。

エレメントの命名

エレメントのクラス名は単語一つです。ハイフンがつかないクラスは必ずエレメントです。

SCSS
.search-form {
	> .field { }
	> .action { }
}

エレメントはコンポーネントによって隠蔽されるので、.titleや.logoなど簡単なクラス名で問題ありません。

複数の単語について

クラス名として単語が2つ以上必要な際は、アンダースコアなどの区切り文字なしで連結してください。

SCSS
.profile-box {
	> .firstname { }
	> .lastname { }
}

推奨セレクタ

クラス名を宣言する際は、子セレクタ(>)の使用を推奨します。こうすることで影響範囲が限定され、パフォーマンスの向上にもつながります。
※子セレクタ(>)をつけると、その親の直下の要素のみ適用されます。子孫にあたる要素には適用されません。

SCSS
.article-card {
	// なるべく子セレクタ(>)をつける
	> .author { }
}

子セレクタの最大のメリットは影響範囲を限定できる点にあります。たとえば、下記のように子セレクタ付きで .article-card > .item、.box-list > .item と定義した場合、.item は .article-card、.box-list それぞれ子供の場合だけスタイルが適用されます。

SCSS
.article-card {
	> .item { }
}
.box-list {
	> .item { }
}
Pug
.article-card 
	.item
.box-list 
	.item

次のように.box-listを.article-card内に移動してみましょう。

Pug
.article-card 
	.item
		.box-list
			.item

.itemが重複しますが、下層のエレメントは上層のエレメントの影響を受けません。この方法は予期しないスタイルの適用を避ける意味で大変有用です。

RSCSSでは、BEM系の命名規則を採用しているアーキテクチャと比べるとクラス名が短いため、名前の重複が起こりやすく、子セレクタの積極的な使用が推奨されます。

タグセレクタを使わない

可能な限りクラス名を使用してください。タグセレクタは禁止ではありませんが、パフォーマンスが少し低下する可能性があり、要素の変更が許されなくなるので、柔軟性にかけます。

SCSS
.article-card {
	// タグに直接設定するのはなるべく避ける
	> h3    {}

	// クラス名に指定するのが良い
	> .name { }
}

Variants

既存のコンポーネントやエレメントと構成が同じで、見た目や機能が違うものを作りたい場合は、新規に作り直すのではなく、バリアントを使います

たとえばコンポーネントの.search-formをベースに、横幅が短いバージョンが必要な際は、バリアントとして作成します。下の図では-prefixed、-compactバリアントを使って.search-formの別バージョンを作成しています。

バリアントの命名

バリアントのクラス名は、バリエーションを表す名称の前にハイフン(-)を付けます。たとえば、.search-formの横幅が短いバーションは.-shortという名称になります。クラス名の先頭にハイフンが付く場合は必ずバリアントです。

SCSS
.search-form {
	&.-wide { }
	&.-short { }
	&.-disabled { }
}

上記はコンポーネントの場合でしたが、エレメントの別バリエーションを作成する場合も同様です。

コンポーネントのネスト

コンポーネントはコンポーネントをネストできます。図のように、.tabarea-containerの中に、.tab-container、.search-formをネストできます。

Pug
.tabarea-container
	nav.tab-container
	.search-form

コンポーネントをネストする必要がある場合のガイドラインを次に示します。

バリアントを使う

コンポーネントを別のコンポーネント内で使う際に、特定の方法で表示したい場合があります。そのような際、ネストされたコンポーネントや、エレメントなどの設定を追加することは避けてください。

SCSS
.article-header {
	// ネストされたコンポーネントに直接エレメントを追加しない
	> .vote-box > .up { }
}

代わりに、ネストされたコンポーネントにバリアントを追加して、そのバリアント内でエレメントを設定してください。

Pug
div.article-header
	div.vote-box.-highlight
	...
SCSS
.vote-box {
	&.-highlight > .up { }
}

ネストされたコンポーネントの簡素化

ネストしたコンポーネントに複数のバリアントを追加していくと、扱いにくくなることがあります。

Pug
div.search-form
	button.search-button.-red.-large

このような場合、ネストされたコンポーネントをエレメントに変更し、@extendを使ってスタイルを設定すると良いでしょう。

SCSS
.search-form {
	> .submit {
		@extend .search-button;
		@extend .search-button.-red;
		@extend .search-button.-large;
	}
}

コンポーネントとエレメントの区分け(追加規則)

ネストされたコンポーネントとエレメントは区分けが難しい場合があります。コーディングの時点では、独立しているか、再利用性があるかなどが明確にはわからないことが良くあります。おススメとしては、区分けルールを細かく規定するより、わかりやすくシンプルにしたほうがRSCSSの特性を活かせて良いと思います。
再利用性の高いサイトであれば、再利性がある場合はコンポーネント、現時点で再利用されいないものは全てエレメントに分類するという方法も有効です。エレメントはコンポーネントに隠ぺいされるため、比較的安全にコンポーネントに転用できます。あまり迷わずフィーリングで切り分けても、後でそれほど困りません。

指針

コンポーネントとエレメントのグレーゾーン区分けは、最終的に利用者にゆだねられています。明確にするのは難しいですが、最低限の指針として下記のようなルールを定めても良いでしょう。

  • 再利用される、もしくはコンポーネントを内包する場合はコンポーネント
  • 再利用されない、要素を含まない場合はエレメント
  • 再利用されない、エレメントを含む要素は状況、プロジェクトに応じて

表にすると下記になります。

再利用される再利用されない
要素を含まないコンポーネントエレメント
エレメント要素のみ含むコンポーネント状況に応じて
コンポーネント要素を含むコンポーネントコンポーネント

レイアウト

コンポーネントは再利用されるので、positionやmarginといった位置や余白に関するプロパティは設定しません。

  • ポジショニング:position、top、left、right、bottom
  • フロート:float、clear
  • マージン:margin
  • 寸法:width、height

※positionについては、子要素のためにrelativeを設定するのは問題ない
※縦横が固定されているエレメントは例外的にwidthやmarginプロパティを設定して良い

コンポーネントの位置を定義したい場合、そのコンポーネントを子セレクタ(>)を使って定義してください。たとえば、.article-cardの位置を定義するには、下記のように親コンポーネントから定義します。

SCSS
.article-list {
	> .article-card {
		margin: auto;
		width: 33.3%;
	}
}

位置とそれ以外のプロパティは別の箇所で定義します。

SCSS
.article-card {
	> .image { }
	> .title { }
}

Helpers

プロパティをオーバーライドしたい場合、別ファイルに集め、クラス名の先頭にアンダースコアを付けます。それらはヘルパーに分類され、通常!importantを設定します。ヘルパーを多用することはお勧めしません。

SCSS
._unmargin {
	margin: 0 !important;
}
._center {
	text-align: center !important;
}

ヘルパーの命名

ヘルパーはオーバーライドしたいプロパティのみ設定するので、必然的に元のコンポーネントと合わせて設定します。

Pug
div.order-graphs ._unmargin

ヘルパーの整理

ヘルパーはすべて1つのファイルに定義します。多い場合は複数のファイルに分離する方が良いですが、ヘルパーの数を最小限に抑えることをお勧めします。

ルールのまとめ

  • コンポーネント名は単語2つをハイフンで連結する
  • コンポーネントにmarginやwidthといったレイアウト用のプロパティを設定しない
  • エレメント名は単語1つ
  • エレメントはコンポーネント内のみ利用可能
  • 可能な限り子セレクタ(>)を使う
  • タグセレクタは使わない
  • 見た目違いはバリアントを使う
  • バリアント名は先頭にハイフンを付ける
  • プロパティのオーバーライドはヘルパーを使う
  • ヘルパー名は先頭にアンダースコア(_)を付ける

ファイル名・ディレクトリ構成(追加規則)

コンポーネントはクラス名毎にファイルを作成します。エレメント、バリアントは独自ファイルを作成せず、ネストされるコンポーネントのファイル内で定義します。

ヘルパーは1つのファイルにまとめます。

├──components
│ └── _コンポーネント名.scss
└──_helpers.scss

コンポーネントのファイル名は先頭にアンダースコア、それに続けてクラス名です。それらのファイルは全てcomponentsディレクトリに設置します。たとえば、.search-formというコンポーネントは_search-form.scss、.article-cardというコンポーネントは_article-card.scssというファイルで定義します。

ヘルパーは_helpers.scssにまとめます。

コンポーネントとは別に、リセットCSS、リセットCSSを補完する要素のカスタム定義など、他プロジェクトでもそのまま使える定義ファイルはbaseディレクトリに設置します。

├──base
│ ├── base.scss
│ └── _ress.scss
├──components
│ └── コンポーネント名.scss
└──_helpers.scss

こららのファイルを読み込むファイルがstyle.scssだとしたら、次のような構成になります。

├──base
│ ├── base.scss
│ └── _ress.scss
├──components
│ └── コンポーネント名.scss
├──_helpers.scss
└──style.scss

style.scssではスタイルを定義せず、base、componentsディレクトリ、_helpers.scssを読み込みます。

ページ毎にスタイルシートを用意する場合

ページ毎に読み込むスタイルシートを変える場合、次のようなディレクトリ構成になります。

├──base
│ ├── base.scss
│ └── _ress.scss
├──components
│ └── コンポーネント名.scss
├──_helpers.scss
├──page1.scss
├──page2.scss
└──page3.scss

このとき注意したいのは、同じパーツで、ページ毎に見た目が違う場合です。RSCSSのルールだとバリアントでバリエーションを作成しますが、CMSなどの都合でページ毎にクラス名を別にできない場合が考えられます。たとえば特定のページだけヘッダをコンパクト表示したい場合、下記のようにバリアント(ここでは-compact)を追加しますが、CMSなどの事情によりクラスを追加できないケースが考えられます。

Pug

.site-header -compact

このようなケースでは、page1.scssなどページ毎に用意するファイルに上書き用の設定を記載すると良いでしょう。

注意点

深いネストを避ける

ネストでの定義は1つまでとしてください。次のような構造になると可読性が低下します。

SCSS
.image-frame {
	> .description {
		/* ... */
		> .icon {
			/* ... */
		}
	}
}

上記のような場合、2階層の設定と3階層の設定を別々にします。

SCSS
.image-frame {
	> .description {
		/* ... */
	}
	> .description > .icon {
		/* ... */
	}
}

可読性についてはどちらが良いともいえないので、きちんと守るか、気にしないのどちらかで統一すると良いでしょう。

単語2つの組み合わせが考えつかない場合

アラートを2単語で表現するのは難しいですが、その場合は特別な意味を持たないサフィックス(接尾辞)を使うと良いでしょう。

.alert-box
.alert-card
.alert-block

インラインの場合も同様です。

.link-button
.link-span

命名規則ガイドライン(追加規則)

コンポーネントの連結する単語の順番

コンポーネントは2つの単語をハイフンで連結しますが、前方と後方のどちらにどの単語を使うか、ルールを決めると良いでしょう。一つの方法としては、前方はその用途を明確にする意味的な単語、後方はbutton、listといった汎用的、機能的な単語にします。

Pug
// 「前の」+「ボタン」
a.previous-button(href="#")

// 「最新の投稿」+「リスト」
ul.latestpost-list

後方の単語は他のクラス名でも共通して使います。

よく使う後方の単語

  • container
  • button
  • list
  • title

領域、範囲、レイアウト関連のクラス名はすべて-container

領域や範囲、レイアウトに使うコンポーネントのクラス名はすべてcontainerで統一します

Pug
main.main-container
	//- 記事
	section.entry-container
		h1.title タイトル


nav.pagination-container
	a.previous-button(href="#" rel="prev")

領域の大小や用途によってwrapper、box、layout、areaといった名前で区別つけません。こうすることで悩むポイントを減らし、シンプルに命名することができます。

どのプロジェクトでもよく使うコンポーネントはすべてdefault

ボタンや一覧など、プロジェクト内で同じようなものが多く使われるコンポーネントについては、汎用的な名称defaultを使ってクラス名を付けます。

.defualt-button
.default-list

ボタンであれば _default-button.scssに.default-buttonと、その派生バリアントをまとめます。

SCSS
.default-button{
	...
	&.-primary{}
	&.-cancel{}
	&.-danger{}
}

バリアントの命名方法

状態を表す.is-activeや、種類を表す.is-primaryなど、よく使うバリアントを紹介します。

カラー

  • -is-primary
  • -is-link
  • -is-info
  • -is-success
  • -is-warning
  • -is-danger
  • -is-light
  • -is-dark

サイズ

  • -is-small
  • -is-medium
  • -is-large

横幅

  • -is-fullwidth

これらはグローバルに使える可能性があるので、ファイルに別途切り出したほうが良いかもしれません。

JavaScriptからの呼び出し

JavaScriptで利用するクラス名にはプレフィックスに js- をつけます。CSSのプロパティ設定はjs-をつけたクラス名には設定せず、元のクラス名の方に設定してましょう。こうすることでCSSとJavaScriptを切り離して管理できます。

Pug
div.menu-toggle.js-menu-toggle

関連記事