RSCSSの概要
RSCSS(Reasonable System for CSS)は、厳選したルールセットのCSSアーキテクチャです。BEMのコンポーネントを参考にComponents、Elements、Variantsの分類が用意されていますが、BEMのとっつきにくいクラス名のルールは使っていません。
CSSアーキテクチャで個性がでるのがクラス名の定義です。RSCSSはすべてにおいてシンプルを目指しているので、クラス名もかなりシンプルなルールになっています。RSCSSのクラスはマルチクラス前提として、クラス名が長くなるのを避けるためにCSSのネストと子セレクタ(>)の利用を推奨しています。
RSCSSの公式ドキュメントにて、RSCSSはフレームワークではなく、保守性の高いCSSを構築するためのアイディアセットと紹介しているだけあって、規則はミニマムです。規定されていない部分を補う必要がありますが、覚えやすく、導入コストが低いのが最大の魅力です。
- 公式ドキュメント
https://rscss.io/
英語ですが、Google翻訳である程度読めます。 - 日本語訳
https://tomcky.hatenadiary.jp/entry/2018/03/19/000139 - 参考テンプレート
https://github.com/zionboogie/pug_rscss
Components
RSCSSのクラスには、Components、Elements、Variantsとい3種類の分類が用意されています。まずはComponentsを紹介します。Componentsは分類の中で一番外枠になる要素で、ヘッダ、ナビゲーション、ボタン、カードなどのUIパーツがコンポーネントに分類されます。
コンポーネントはどこにでも置くことができ、コンポーネント内にコンポーネントを配置することもできます。
コンポーネントの命名
コンポーネントは、ハイフンで連結された最低2つの単語で命名されます。コンポーネント以外のエレメントやバリアントはハイフンを使わないので、クラス名にハイフンがあればそれはすべてコンポーネントです。
コンポーネントの例:
- いいねボタン(.like-button)
- 検索フォーム(.search-form)
- ニュース記事カード(.article-card)
- 名前空間付きコンポーネント(.rico-custom-header)
Elements
エレメント(要素)はコンポーネントを構成する子要素です。エレメントは自分が属するコンポーネント内でのみ利用可能で、独立して使われることはありません。
検索フォームのコンポーネントに内包されるテキストボックスやボタンがエレメントです。また、上の図の.tabのように、コンポーネント内で何度でも繰り返し利用できます。
エレメントの命名
コンポーネントのクラス名は2つの単語をハイフンで連結しましたが、エレメントのクラス名は単語一つです。ハイフンがつかないクラスは全てエレメントです。
SCSS
.search-form {
> .field { }
> .action { }
}
エレメントはコンポーネントによって隠蔽されるので、.titleや.logoなど簡単なクラス名で問題ありません。
複数の単語について
クラス名として単語が2つ以上必要な際は、アンダースコアなどの区切り文字なしで連結してください。
SCSS
.profile-box {
> .firstname { }
> .lastname { }
}
推奨セレクタ
クラス名を宣言する際は、子セレクタ(>)の使用を推奨します。こうすることで影響範囲が限定され、パフォーマンスの向上にもつながります。エレメントは.logoや.titleといった簡単な名前になるので、理由がなければ子セレクタ(>)を必ずつけて、重複を避けるようにしましょう。
※子セレクタ(>)をつけると、その親の直下の要素のみ適用されます。子孫にあたる要素には適用されません。
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;
}
}
SCSSのファイル名・ディレクトリ構成
RSCSSでは、コンポーネントはクラス名毎にそのクラス名でファイルを作成します。これがRSCSSの一つの特徴となっています。クラス名とファイル名が同じ便利さは一度やってみると実感できると思います。想像していた以上に編集が楽になります。
なお、コンポーネントとは違い、エレメント、バリアントは独自ファイルを作成せず、ネストされるコンポーネントのファイル内で定義します。ヘルパーは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などページ毎に用意するファイルに上書き用の設定を記載すると良いでしょう。
ディレクトリ構成
PugやScss、JavaScriptといった元ソースと、出力先のディレクトリ構成の例を下記に記載します。元ソースファイルは_srcディレクトリにまとめ、HTMLやCSSといった出力ファイルはプロジェクトディレクトリの直下に置きます。CSS、画像、JavaScriptはさらにサブディレクトリを用意して、commonディレクトリにまとめます。
├──_src
│ │
│ ├──asset
│ │ │
│ │ ├──css
│ │ └──js
│ └──include
│
└──common
│
├──css
├──img
└──js
_src以外がリリース用ファイルとなります。これ以外でも、リリース用ファイルをdistディレクトリに集めても良いと思います。この方法の難点としては、distがドキュメントルートになるので、環境によってはプレビューが若干面倒になります。
├──_src
│ ├──asset
│ │ │
│ │ ├──css
│ │ └──js
│ └──include
│
└──dist
│
└──common
│
├──css
├──img
└──js
コンポーネントとエレメントの区分け(追加規則)
ネストされたコンポーネントとエレメントは区分けが難しい場合があります。コーディングの時点では、独立しているか、再利用性があるかなどが明確にはわからないことが良くあります。おススメとしては、区分けルールを細かく規定するより、わかりやすくシンプルにしたほうが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つまでとしてください。次のような構造になると可読性が低下します。
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
参考テンプレート
上記ルールで作成したテンプレートを公開します(SCSS、PUG利用)。_src/asset/css/にCSS関連のファイルをまとめています。