ReactではCSSのスコープ課題を克服する「CSS Modules」がおすすめ
CSSファイルの一番の問題はグローバル定義がされてしまう事で、WEBコンテンツ全体に影響範囲(スコープ)が及ぶことです。
通常のCSSやインラインスタイル(Inline styles)ではこのスコープの問題を解決できなかったり、機能不足な面があります。
CSS Modulesは従来CSSと同じレベルで使えて、さらにクラス名にプレフィックスがつくため、CSSのスコープが他のコンポーネントに及ぶ事がありません。
ReactでのCSSの扱い方
CSSファイルの課題
CSSはclass名とプロパティ・値を設定し、タグ内にclass名を記述して適用させるファイルです。
これにより自由にWEBアプリをデザインできる訳ですね。
React開発でももちろん、様々な方法でCSSを活用します。
Reactでは、管理者を悩ませていたCSSの抜本的な課題を克服できる様に改変されています。
では従来から抱えていたCSSファイルの課題とは何でしょうか。
CSSはグローバルに適用される
それはCSSが「グローバル定義」されるファイルである事です。
設定したCSSスタイルは、常にWEBアプリ全体を効果の対象範囲に含んでしまいます。
例えばCSSで「pタグ」に直接スタイリングをした場合、そのCSSを読み込む限りpタグ全てに適用がされてしまいますよね。
※極端なお話pタグの色を「赤」にした場合、WEBサイトの全てのpテキストが赤文字になってしまいます。
つまり本来設定したいところ以外にも、意図せず反映がされてしまう場合があるのですね。
管理者が任意にコントロールするしかない
これは管理者が気を付けていくしかありません。アプリ側は一切関知しないためです。
ですので管理者はその効果が自分の意図する部分にのみ適用されるように、タグにクラス名をつけて管理します。
その影響範囲(以下:スコープ)を限定しようとコントロールする訳です。
大量のクラス名でわかりにくくなる
しかしこれにより、クラス名が大量に設定され非常に複雑・肥大化してしまいます。
効率よく使い回そうとする程、知らないうちに違う場所でスタイルが適用されるリスクが増える訳です。
修正箇所を特定するのが大変
仮にスタイルのミスに後から気づいても、どこを直せば良いのかすぐには把握できませんよね。
毎回CSSファイル全体に検索を掛けるのは、保守の観点上決してよくはありません。
F12の管理者ツールなどで適用されたスタイルを探しながらの修正作業になる訳です。
コンポーネント範囲に収めるReactのCSS
ReactでのCSS適用方法には、いくつかの手法があります。
一番の目的は、その影響範囲(スコープ)がコンポーネントの外に出ない様にする事です。
本記事ではまず「通常CSS」「インラインスタイル」「CSS Mojules」についてご紹介します。
それ以外にたくさんの記述方法があります。
一番のおすすめは「CSS modules」になりますね。
CSS Modulesを紹介する前に、まずは通常のCSSやインラインスタイルに簡単に触れておきましょう。
通常のCSS
これはcssファイルにclassを定義してhtmlに適用する、まさに通常のCSSです。
通常のWEBサイト制作で使う手法と全く同じCSSですね。
React開発でもHTML+CSSの様に、ヘッダー部分にCSSファイルを読み込んで反映させる事ができます。
従来と同じなので楽なのですが、グローバル定義になってしまう事は避けられません。
CSSの書き方
コンポーネント側のjsxファイル内に、className={クラス名}を設定して適用します。
.jsxファイル
import React from "react"; const midashi = () => { return ( <h2 className={"midashi"}>見出しテキスト</h2> ); }; export default midashi;
.css(アプリのヘッダーで別途読込)
.midashi { font-size: 24px; padding: 1em; background: #999; }
上記にCSSの読込記述はありませんが、アプリのヘッダーファイル部分でCSSファイルが読み込まれていれば適用されます。
midashiクラスのスコープは全体に及ぶ
ただこの場合、midashiクラスのスコープはアプリ全体に及んでしまいます。
別のコンポーネント作成時に同じmidashiを定義してしまい、バッティングする可能性も出てきますね。
通常のcss管理もそうですが、命名規則や「BEM」などの重複を避けるための工夫が必要でしょう。
インラインスタイル
次に紹介するのがインラインスタイル(Inline Styles)です。
インラインスタイルは、jsやjsxファイルに直接CSSを記述する方法です。
インラインスタイルには以下の2つの書き方があります。
・直接タグ内に記述する方法
・事前に定義した上で適用する方法
いずれもHTML上にstyle属性をつけ、クラス名を設定して適用します。
jsファイル内に記述するため、CSSのプロパティと値は「Javascriptオブジェクト」の形で書く必要があります。
直接タグ内に記述する
HTMLソース内の指定位置にstyle={{}}をつけ、中に直接プロパティと値を入れます。
直接記述の例
return( <div style={{ width: "100%", marginBottom: "20px" }}> <p style={{ color: "blue", textAlign: "center" }}>テキスト</p> </div> );
やたらと長くなりがちな書き方になりますね。
事前に定義した上で適用する
事前にクラス名を定義しておき、スタイリング設定を分けておく方法です。
HTMLソース内の指定位置にstyle={}を入れ、その中に定義したクラス名を入れて反映させます。
事前定義した例
const divStyle ={ width: "100%", marginBottom: "20px" }; const textStyle ={ color: "blue", textAlign: "center" }; return( <div style={divStyle}> <p style={textStyle}>テキスト</p> </div> );
いずれもプロパティが連続する場合は「,」で区切っていきます。最後は「,」をつけません。
インラインスタイルの注意点
プロパティはキャメルケース表記
まず大前提として、プロパティ名は「キャメルケース」表記で書きます。
キャメルケースとはラクダの「こぶ」の様に、単語の真ん中付近に大文字が入る書き方です。
-を削除して大文字でつなげる
例えばCSSにはtext-alignやborder-leftの様に、間に「-」が入るプロパティがありますよね。
Reactのjsではこの「-」部分が認識されないため、書いてはいけない決まりがあります。
ですのでプロパティの「-」部分を削除し、次に続く文字を大文字にして記述します。
・textAlign
・borderLeft
・maginTop
値は文字列か数値で
プロパティの後ろに来る値はleftやred等の名称であれば、""で区切る必要があります。
color: "#cc0000",
fontWeight: "bold",
marginLeft: "30px"
数値のみであれば、""なしで設定しても構いません。
marginBottom: 0
インラインスタイルのメリットデメリット
メリット
導入が非常に簡単です。キャメルケースと記述の仕方さえ覚えておけばすぐに導入できますね。
jsファイルに直接書くので、そのスコープが対象コンポーネントの外に出る事はありません。
デメリット
インラインスタイルは何かとコードが煩雑になりやすくなる面があります。
毎回Javascriptのオブジェクトを生成するため、比較的パフォーマンスが悪くなります。
一番のネックは、インラインスタイルではCSS擬似要素を使用する事ができない事でしょう。
これでは限界がありますね。
小さく簡単なアプリならともかく、相応の大きさのアプリ開発では難しいでしょう。
おすすめはCSS Modules
ではいよいよCSS Modulesの紹介です。
これも基本的には従来のCSS同様、.cssや.scssファイルに記述します。
CSS Modulesのファイルは、コンポーネントごとに用意するという特徴があります。
node-sassのインストール
CSS Modulesを利用するためには、モジュールのインストールが必要です。
scssの形式で記述もできる様に「node-sass」のインストールをお勧めします。
npmでの例
以下はインストールにnpmを使った場合です(yarnやCodeSandboxからでもOK)。
コマンドラインを展開し、cdでReactプロジェクトフォルダへ進みます。
以下のインストールコマンドを入力しましょう。
npm install node-sass
インストールが完了すれば、CSS Modulesの準備はOKです。
CSS Modulesの使い方
基本的にはコンポーネントとなるファイルと1対になる様にCSSファイルを作成します。
「App.js」1枚
に対して
「App.module.scss」1枚
ファイル名はかならず、~.module.scss(.cssでも可)にする必要があるので注意しましょう。
scssの書き方
scssファイル内はWEBサイトで使うscss(css)と何ら変わりはありませんので、普段通りに記述ができます。
キャメルケースや""で囲う必要もありません。
App.module.scssの読込
.container{ border: solid 1px #999; padding-bottom: 22px; background: #cc9999; } .text{ color: #fff; font-size: 1.2rem; }
App.jsの記述
CSSクラスを使用するコンポーネント(App.js)の上部で、モジュールファイルを読み込みます。
import classes from "./App.module.scss"; export const App = () => { return( <div className={classes.container}> <p className={classes.text}></p> </div> ); };
ここでは、App.module.scssをimportする名称を「classes」としています。
各タグにclassName={}と属性を入れ、中にclasses.に続けて「クラス名」を指定します。
スコープがコンポーネント単位にするための工夫
CSS Modulesでは、そのスコープはコンポーネント内にとどまります。
勘の良い人は、最終的にアプリとしてビルドした時に同じクラス名があったら影響があるのではないかと思う事でしょう。
Aコンポーネント内css….containerクラスを設定
Bコンポーネント内css….containerクラスを設定
→最終的にビルドした時にかぶるのではないか?
Reactではその心配はありません。
仮に同じクラス名が、各コンポーネント内CSSファイルにそれぞれ1つずつ存在していても大丈夫です。
クラス名にプレフィックスが付与
それはReactのビルド時に、本来のクラス名に続けてプレフィックス(ランダムな英数字)が付与されるためです。
これにより同じクラス名ではなく「唯一無二のクラス名」に変更される訳です。
クラス名にプレフィックス情報が付与された状態
CSS Modulesはメンテナンス性も高い
CSS Modulesはグローバル定義ではないので、対象範囲外にスコープが及ぶ事がありません。
コンポーネントが対とするCSSが決まっていますので、CSS全体を検索したりする必要がないのです。
クラス名で検索する必要が無く、対象コンポーネント内のCSSを開いて修正するだけなのでメンテナンス性に優れています。
プロジェクト全体の方向性など大きな縛りが無いのであれば、CSS Modulesはお勧めです。