Vite で CSS Modules のクラスが適用されない
Vite は `*.module.css` / `*.module.scss` のファイル名を CSS Modules として自動処理するが、命名が外れている、import から `styles.foo` を経由していない、`css.modules.localsConvention` のずれでケバブケース名が取れない、などで適用されないことがある。
公開:
要約
Vite で CSS Modules が効かない場合、ほぼ確実に ファイル名が *.module.css パターンになっていない か、JS 側で styles.foo を経由していない のどちらか。vite.config.ts で挙動を変えるのは命名規則の調整が必要な時のみで、まず命名と import 方法を確認する。
よくある原因
- 拡張子ミス:
Button.cssのままだと通常 CSS として全コンポーネントにグローバル適用される - import 経由していない:
import './Button.module.css'のような副作用 import だけだとクラス名はハッシュ化されているので、そのまま書いたクラス名にはマッチしない - camelCase 期待:
.foo-barクラスをstyles.fooBarで取りたいのにlocalsConventionが camelCase 系になっていない - PostCSS の順序: 他のプラグインが先に走ると CSS Modules のハッシュ化結果が壊れる場合がある
解決策
1. 命名を .module.css にする
src/components/Button.module.css ← OK
src/components/Button.css ← 通常 CSSVite の Features ガイド のとおり、*.module.{css,scss,sass,less,styl,stylus,pcss,postcss,sss} がデフォルトの判定パターン。
2. JS 側で styles.xxx を経由する
import styles from "./Button.module.css";
export function Button() {
return <button className={styles.button}>OK</button>;
}副作用 import だけだと CSS は読み込まれるが、クラス名はビルド時にハッシュ化されているため <button className="button"> と書いても当たらない。
3. ケバブケース → camelCase 変換
Button.module.css で .foo-bar と定義したクラスを JS から styles.fooBar で取りたい場合:
// vite.config.ts
import { defineConfig } from "vite";
export default defineConfig({
css: {
modules: {
localsConvention: "camelCaseOnly",
},
},
});camelCaseOnly は元のケバブケース名を消すため、両方欲しい場合は camelCase を使う。
詳細は Shared Options のリファレンス の css.modules を参照。
4. PostCSS との競合
postcss.config.js で postcss-import などを使っている場合は CSS Modules 処理の前に解決させる。
module.exports = {
plugins: [
require("postcss-import"),
require("autoprefixer"),
],
};postcss-import を plugins 配列の先頭に置くと、@import 解決後に Vite が CSS Modules 化する。