できない.dev

Vite で CSS Modules のクラスが適用されない

Vite は `*.module.css` / `*.module.scss` のファイル名を CSS Modules として自動処理するが、命名が外れている、import から `styles.foo` を経由していない、`css.modules.localsConvention` のずれでケバブケース名が取れない、などで適用されないことがある。

#css-modules#css#postcss#styling

公開:

要約

Vite で CSS Modules が効かない場合、ほぼ確実に ファイル名が *.module.css パターンになっていない か、JS 側で styles.foo を経由していない のどちらか。vite.config.ts で挙動を変えるのは命名規則の調整が必要な時のみで、まず命名と import 方法を確認する。

よくある原因

  1. 拡張子ミス: Button.css のままだと通常 CSS として全コンポーネントにグローバル適用される
  2. import 経由していない: import './Button.module.css' のような副作用 import だけだとクラス名はハッシュ化されているので、そのまま書いたクラス名にはマッチしない
  3. camelCase 期待: .foo-bar クラスを styles.fooBar で取りたいのに localsConvention が camelCase 系になっていない
  4. PostCSS の順序: 他のプラグインが先に走ると CSS Modules のハッシュ化結果が壊れる場合がある

解決策

1. 命名を .module.css にする

src/components/Button.module.css   ← OK
src/components/Button.css          ← 通常 CSS

Vite の 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.jspostcss-import などを使っている場合は CSS Modules 処理の前に解決させる。

module.exports = {
  plugins: [
    require("postcss-import"),
    require("autoprefixer"),
  ],
};

postcss-importplugins 配列の先頭に置くと、@import 解決後に Vite が CSS Modules 化する。

この記事は役立ちましたか?