🏗️

CSS設計「REMM」を紹介したい

2022/12/21に公開

プロローグ

皆さんはCSS設計に何を採用していますか?
最近は Scoped CSS や Tailwind CSS などを使用し、そもそもCSS設計について考えることが減ってきているかもしれません。
それでも、たまにCSS設計を意識しなければいけない場面には多々遭遇しますよね。

CSS設計といえば有名なのは、BEM、FLOCSS、OOCSS、SMACSS、あたりでしょうか。
私も今までに様々なCSS設計を試してきました。
しかし、どれもこれも私には馴染まなくて、採用を断念してきました。

そんなとき、REMMというCSS設計に出会いました。
ガイドラインを読んでみて一目惚れし、すぐにプロジェクトで試しに使用してみてまた惚れ直しました。

今までのCSS設計は、端的に言えば「ガチガチに定められたルールに雁字搦めにされて窮屈」に感じてしまい、私には合いませんでした。
それに対し、REMMは「自由度が大きく、プロジェクトによって柔軟に設計を変えることのできるゆとり」があります。
それでもなお、破綻しないように細かいところまで気が配られています。

まさに私が求めていたCSS設計でした。
「なんで私はCSS設計をうまく使いこなせないんだろう」と悄気げる毎日でしたが、REMM によってその理由も浮き彫りになったようです。

REMM の概略

正直、この記事で改めて解説しなくても良いんじゃないかというくらい、公式のガイドラインがこれ以上にないくらい詳しく書かれています。
サンプルコードとともに様々な利用ケースを想定して網羅されています。
https://remm.qwiproject.com/

公式ガイドラインだけ貼って「読んでみてね。はい、おしまい!」というわけにはいかないので、特徴的な項目だけいくつかピックアップして紹介したいと思います。

REMM とは

ICT株式会社によって考案されたCSS設計で、記事執筆現在よりも3年半前の 2019年5月29日 にガイドラインが公開されました。結構経ってますね。
それでも、今でも BEM がまだ使用され続けているのと同様に、 REMM も今でも通用するCSS設計だと思っています。

特徴

プレリリース[1]からの引用ですが、下記のような特徴があります。

  • HTMLの構造に左右されない概念的なグルーピングによって、コードの把握やCSSの管理が簡便になる
  • 予測しやすさ、冗長さ回避、拡張しやすさ、影響分離をバランスよく「良いところ取り」できる
  • 同じ記法でありながらも、サイトやプロジェクトの特性に応じてCSS設計を使い分けできる
  • 対象の内側や外側を自由に拡張でき、離れた場所にある関連部品も同じSassファイルで集約管理できる
  • 命名が冗長になりにくく、「予約語」のアプローチで、さらに命名の冗長化を防げる
  • Bootstrapなど、ケバブケースを採用したCSSフレームワークとの親和性が高い

「Roots」「Element」「Modify」「Modifier」

REMM は「Roots」「Element」「Modify」「Modifier」の頭文字を取った名前となっています。

Roots

BEM の Block と似ているように見えますが、潜在的な意味合いは異なります。
Roots は役割の起源となる単語や文字列で、要素の命名とCSS管理をより簡便にするための考え方です。
あくまで要素そのものではなく、概念の名前であるという広い括りの考え方になります。

Element

Elementは前にある語句の専用部品や要素です。
BEM の Element と似ていますが、HTMLの物理的な構造に縛られないのが特徴です。

Modify

Modifyは変化版やバリエーションです。BEM の Modifier と似ています。
属する Roots によってグルーピングされます。

Modifier

Modifier は修飾を動機とした後付けのclassです。

記述ルール

Element の前にはハイフンを2個繋げて、Modifier の前にはハイフンを1個繋げるのが基本です。
たとえば roots--element-modifier のように連結します。

複数単語の場合は、スネークケースもしくはキャメルケースを使用します。
例えば下記のようになります。

  • roots_name--element_name-modifier_name
  • rootsName--elementName-modifierName
ハイフンとアンダースコアの使い方について、BEM との比較

BEM はハイフンやアンダースコアの使い方のせいで、文字選択の際にとても不便を感じていました。
複数単語はハイフンでつなぎ、Element の前にはアンダースコアを2つ繋げる命名で、例えば block-name__element のようになります。

意味合いとしては block-name に属する element なので、文字選択する際もこの区切りで選択したいことが多いです。
しかし、block-name を選択しようと思ってダブルクリックすると、 name__element の範囲が選択されてしまいます。
本当に不便でした。BEMを使わなくなった理由の半分くらいはこれが原因かもしれません。

その点、REMMは文字選択の不便がなくなるようなハイフンとアンダースコアの使い方を定義しています。それだけでも非常に高ポイントです。
もちろん偶然ではなく、意識してそういった命名にしています

連結

Roots が先頭に1回だけ使われるのは BEM の Block と同じですが、その後に続く Element と Modify は何個でも連結できます。

おさらいしますと、Element は前の文字列に対する専属部品、Modify は前の文字列に対するバリエーション、という意味で使用されます。
なので、 Element の後にまた Element を繋げても良いですし、Modify の後にさらに Element を繋げることもできます。
おそらく Modify の直後に Modify だけできないのだと認識しています。(そもそも、あまりそういう場面は多くなさそう)

というわけで、下記の命名は全て有効なものです。
card が Roots、それに属する buttonicon が Element になります。
また、cardlargebuttonprimaryiconred はそれぞれ Modify です。

  • card
  • card--button
  • card--button-primary
  • card-large
  • card-large--button
  • card--button--icon
  • card--button--icon-red
  • card--button-primary--icon
  • card--button-primary--icon-red

Modifier の命名

先述の Modify と名前が似ていて紛らわしいですが、異なるものです。
Modifier は修飾用として利用するもので、製作者の動機で自由に使用できます。
命名にルールはありません。

  • card hoverhover が Modifier)
  • card--button is-focusis-focus が Modifier)
  • card--button-primary js-triggerjs-trigger が Modifier)

Roots に接頭辞を追加する

Guide | Rootsに対するプレフィックス(接頭辞)の考え方

SMACSS や FLOCSS を使用している場合、Roots に接頭辞が必要になるかもしれません。
例えば、l-headerc_footer などです。

このような接頭辞の目的は、その役割や意味を人が把握するためのものです。
REMM では、このような Roots の接頭辞は、元の単語の一部として捉えることができます。
このときの連結記号には、ルールや制限はありません。

バリエーション作成の例

card というカードコンポーネントを作るとしましょう。

<div class="card">カード</div>

card のバリエーションを増やす方法の1つは、card にModifyを追加します。

<div class="card-large">カード</div>

これはマルチクラスにすることも可能です。

<div class="card card-large">カード</div>

Modify ではなく Modifier としてバリエーションを増やすこともできます。

<div class="card large">カード</div>

このように複数の方法を利用できるため、プロジェクトで利用している設計に応じて適切なものを選択することができます。

予約語のアプローチ

Guide|単語の汎用化|予約語のように扱う

icontext など、あらゆるコンポーネントで繰り返し使用される小さな部品もあるかと思います。
こういったものは、card--iconform--text などのように、別の Roots に属する Element として定義することで、スタイリングの競合を避けることができます。
しかし、命名が冗長になったり、text があちこちの Roots に出てきて form--text やら card--text やらいちいち定義するのが煩わしい、なんてことに陥りやすくなってしまいます。

予約語とは、上記の懸念点を解決するためのアプローチです。
icontext などのような繰り返し使用される語句を Roots として定義し、scssファイルを作成します。
しかし、ここで特別なスタイリングは行いません。

text.scss
//text
//--------------------------------
//意味付け用の「予約語」として使用し、グローバルなスタイルは与えない。

//Roots
.text {}

あらかじめこのようなファイルを用意しておくことで、 text という Roots が予約語であることを示せますので、役割が明確になります。
つまり、scss ファイル自体がドキュメントの変わりになります。

text にスタイルがあたっていないことが明確になったので、別の Roots の中で改めてスタイルの競合を気にすることなく .form--text ではなく .text のクラスでスタイリングできます。

fotm.scss
//form

//Roots
.form {
    ...
}

.form .text {
    font-size: 1.2rem;
}

予約語としていた Roots そのものを拡張することもできます。

text.scss
// text
// --------------------------------
// 意味付け用の「予約語」として使用し、グローバルなスタイルは与えない。

// Roots
.text {}

// 成功系のテキスト
.text-success {}

// 失敗系のテキスト
.text-error {}

また、後から別のアプローチに変更することも容易です。
例えば、text の基本となるスタイルを与えておいて別の Roots で差分をスタイルする、といったアプローチに切り替えることも、Roots を定義するこの scss ファイルの存在のおかげでスムーズに行えるでしょう。

Element の記述位置

HTML上で、Element は Roots の中に書かないといけないと思うもしれません。
REMM の Roots はあくまで概念であり、BEM の Block のように要素そのものを指すわけではありません。
そのため、Roots に所属する Element は、必ずしもその Roots の中にしか記述できないわけではありません。

Element を Roots の外側に拡張

例えば下記のような heading について、アウターやラッパーを作成するために外側へ拡張することができます。

<div class="heading--outer">
    <div class="heading">
        <i class="heading--icon"></i>
        <h2 class="heading--title">見出しのテキスト</h2>
    </div>
</div>

Element を Roots の兄弟関係に記述

下記のようなコンポーネントがあったとします。
branding という Roots で概念化されています。

<div class="branding">
    <div class="branding--logo"><img src="" alt=""></div>
    <h1 class="branding--name">サイト名</h1>
    <div class="branding--catch">
        <p>サイトのキャッチコピー</p>
    </div>
</div>

このとき、デザインや実装の都合でキャッチコピーだけ外に出したい場合があったとしましょう。
REMMは、Roots の兄弟関係の位置に Element を置いても問題ありません。

<div class="branding">
    <div class="branding--logo"><img src="" alt=""></div>
    <h1 class="branding--name">サイト名</h1>
</div>
<div class="branding--catch">
    <p>サイトのキャッチコピー</p>
</div>

離れた位置に記述

同じ Roots に属する Element を、全く離れた位置に置いても問題ありません。
下記は、メニューのボタンとメニュー本体を離れた位置に置いた例です。

<body>
    <header>
        <a class="side_menu--btn-toggle" id="js-side_menu--trigger"></a>
    </header>
    <main></main>
    <footer></footer>
    <div class="side_menu--container" id="js-side_menu--target">
        <!--スライドメニューの内容-->
    </div>
</body>

.side_menu という class は使用されていませんが、side_menu はあくまで Roots であり概念なので、使用していなくても問題ありません。

エピローグ

REMM の特徴の中でも、他のCSS設計とは違うなあと思ったものの一部を紹介してみました。
同じことを繰り返しますが、REMM を使用している中でも「おや?」と思いがちなことから「どうしたら?」といったことまで、様々なユースケースを想定して公式ガイドラインが書かれています。
こんな記事を見るよりも公式ガイドラインを見てもらった方が REMM に対する理解は深まります。

結局のところは各々のプロジェクトの実装との相性や、開発者自身の好みの問題に落ち着きますので、REMM の使用を強要することはしません。
それでも「こんな設計もあるんだなあ」と知ってもらえたならば、REMM を3年愛用してきた私にとって冥利に尽きます。


脚注
  1. あらゆるウェブページ制作に対応!CSSコーディングメソッド「REMM(レム)」の情報サイトを公開|ICT株式会社のプレスリリース ↩︎

Discussion