CSS Modules とは
CSS Modules は「普通の CSS の書き方はそのまま」で、コンポーネントごとにスタイルが衝突しないようにする仕組みです。
導入方法(Vite)
ファイル名を .module.css にするだけで、Vite が自動的に CSS Modules として扱ってくれます。
通常の CSS と比較して、書いている CSS 自体は全く同じです。違うのは「クラス名を文字列で直接書く」か「import したオブジェクトのプロパティとして参照する」かという読み込み側のコードだけです。
/* Button.module.css */
.button {
background-color: #2563eb;
color: white;
}
// Button.tsx
import styles from './Button.module.css'
function Button() {
return <button className={styles.button}>Click me!</button>
}
普通の CSS と比較して何が嬉しいのか
■ クラス名の衝突が発生しない
ここが一番重要な違いです。
普通の CSS の場合、.button というクラス名はアプリ全体でグローバルです。別のファイルでも .button というクラス名を使ってしまうと、後から読み込まれた方のスタイルで上書きされてしまいます。
/* Card.css */
.button {
background-color: red; /* Button.css と同じクラス名 */
color: white;
}
これを防ぐために、大きいプロジェクトになるほど「クラス名の命名規則(BEM など)」を人間が頑張って守る必要が出てきます。
CSS Modules では、ビルド時にクラス名が自動的にユニークな文字列に変換されます。別の .module.css ファイルで同じ .button というクラス名を使っても、実際に出力されるクラス名は異なるものになり、衝突しません。
ブラウザの開発者ツールで見ると、class="_button_e3xlo_1" のようなユニークな名前が付いているのが確認できます。
<button className={styles.button}>Click me!</button>
CSS Modules の限界
CSS Modules は静的な CSS ファイルなので、props や state といった実行時の値をスタイルに反映することはできません。
例えば、ボタンの色を種類ごとに変えたい場合、あらかじめクラスを複数用意して、JS 側で出し分ける必要があります。
/* Button.module.css */
.primary {
background-color: #2563eb;
}
.secondary {
background-color: #e5e7eb;
}
function Button({ variant }: { variant: 'primary' | 'secondary' }) {
return <button className={styles[variant]}>Click me!</button>
}
パターンが少なければ問題ありませんが、組み合わせが増えたり、数値を連続的に変えたい(進捗バーの幅を 0〜100% で変える、など)場合、クラスの数だけでは対応しきれません。
この「動的なスタイル」を扱うために生まれたのが CSS-in-JS です。以下の記事では CSS-in-JS について解説していますので、併せてご覧ください。
