linotice

linotice

2016.11.07

BEMの良さを活かす、CSS Modulesのベストプラクティスを探ってみた

画像
9月14日に開催されたマークアップ~フロントエンドの勉強会SCRIPTY#6 ~フロントエンド紳士・淑女のための勉強会~
2番目にご紹介するのは、デザイナー/フロントエンドエンジニア柴田和祈によるセッション「CSS Modulesのベストプラクティスを探る」というタイトルで行われた発表の概要です。

なぜ、CSS Modulesを使うことにしたのか

現在は、Yahoo! JAPANの広告入稿管理ツールのフロントエンドを担当し、主にSPAを作っています。今回は、担当しているプロジェクトにCSS Modulesを活用した際の知見をお伝えしたいと思います。

image
▲メディア・マーケティングソリューショングループ 柴田 和祈

まずCSSの問題点を考えてみると、スコープがグローバルであること、それを解決するためにBEMなどの手法を用いるとclass名が冗長になってしまうこと、などが挙げられます。

この問題解決のために、2014年にReactにおいて定評されたのがCSS in JS。CSSをJSのオブジェクトとして定義し、Component側からimportすることが可能で、JSファイルとしてCSSを管理するため、ローカルなスコープです。

しかし、classの重ねがけをするのにオブジェクトのマージが必要であったり、セレクタの優先度もないので、後からマージしたもの勝ちになる、疑似要素や:hoverなどが使えないなどといった課題がありました。

そこで登場したのが、CSSファイルをJSにimportすることができるCSS Modulesです。疑似要素や:hoverを使うことも可能で、プラグインで記法をカスタマイズすることもできます。

image


内部では何が起こっているのかというと…

JSからCSSをimportすることで、webpackのcss-loaderによって、最終的にHTMLの「head」タグ内の「style」としてインライン挿入されるようになります。

また、セレクタ名がBase64変換されるため、他のセレクタとかぶりを心配する必要がなくなり、webpack.config.jsで[ファイル名]_[クラス名]_[ランダム値]といったように形式を指定することもできるようになります。

このような形でCSSを定義して読み込むと、以下のようになります。

image
image


CSSの記法でBEMの良さを活かすには

BEMとは、Block(親)、Element(子)、Modifier(状態)を組み合わせて行うCSSの設計思想です。

BEMのルールに従い、Sassを使うと以下のようなCSSを吐き出すことができます。

image

これをCSS Modulesを使って、「ファイル名__クラス名___ランダム値」といった形で吐き出すとこのようなCSSが吐き出されます。

image

先頭のファイルとBlock名(.Nav)が重なって冗長なため、webpack.config.jsで「クラス名___ランダム値」という形式でCSSを吐き出したのが以下です。

image

しかし、実際にReact側で動かしてみると、Syntax Errorが出てしまいます。これはJSではハイフンを変数名として使えないから。そこで、modifierをアンスコ(_)に、ランダム値をハイフン(-)にしてみたところ、問題なくいけました。

image

さらに今度はBlock名も消してみたところ、冗長な部分もなくなり、Syntax Errorも出ず、React側も問題なくだいぶスッキリした状態になりました。

image


CSS Modulesのベストなディレクトリ構成は?

CSS Modulesのディレクトリ構成は何がベストなのか、考えてみました。一つ目の方法はコンポーネントの隣にCSSを置くこと。

image

同じ階層にCSSを配置するとかぶりがあり、冗長な部分が出てきてしまうため、共通部分を別CSSに切り出してみました。

image

上記のようにAlert.js / Submit.js両方からimportする方法もあるのですが、切り出したContainer.cssに対応するContainer.jsというコンポーネントを作ってあげるほうがよいのではないかと考えています。

image

CSSが共通化できるということは、コンポーネントも共通化できるはず。なぜならどちらもViewをつかさどっている部分なので、CSSができてJSのViewができないわけはないというのがその理由です。

二つ目の方法は、コンポーネントと同じ階層構造のディレクトリを作ること。

image

これはJSからimportする際に相対パスをたどるのが大変という点もあるのですが、同階層のパターンと比べて融通は効きやすいというメリットがあります。

結論としては、ディレクトリ階層は上記どちらでも良さそうということです。

* 当日資料「CSS Modulesのベストプラクティスを探る」はこちら

採用情報 公式SNSアカウント

このページの先頭へ