本文へスキップ
ウェブエンジニア問題集
第13章

MUIと他のCSSを共存させる — カスケードレイヤーとTailwind CSS併用

約8分
この章の目次開く

「MUIのボタンにTailwindのクラスを当てたのに効かない」——これは設定ミスではなく、MUIのスタイルの作られ方から生じる当然の結果です。

この章では原因と、v9で用意されている解決策を扱います。

学習者学習者

className="bg-red-500" を付けたのに色が変わらない。!important を付けたら効いたけど、これでいいの?

動きはしますが、!important を積み重ねると、あとから誰も上書きできない状態になります。もっと素直な方法があります。


なぜ外からの上書きが効かないのか

MUIのスタイルはEmotionが実行時に生成します。生成されるセレクタは、コンポーネントのクラスと組み合わさって詳細度が高くなりがちです。

一方、Tailwindのユーティリティクラスは単一クラスなので詳細度が低く、素直に書くと負けます。

詳細度の勝負で殴り合うのではなく、勝ち負けのルール自体を変えるのが正解です。そのための仕組みがCSSのカスケードレイヤーです。


カスケードレイヤーという考え方

カスケードレイヤー(@layer)は、CSSの優先順位を「詳細度」より先に決める仕組みです。

重要な性質が2つあります。

  1. レイヤーの順番が詳細度より優先される。 後ろのレイヤーが前のレイヤーに勝つ
  2. レイヤーに入っていないスタイルは、どのレイヤーよりも強い

2つ目が直感に反するので注意してください。レイヤーの外にあるスタイルは、レイヤー内のスタイルに必ず勝ちます。

つまり、MUIのスタイルをレイヤーに入れてしまえば、レイヤー外の自前CSSはそれだけで勝てるようになります。詳細度を気にする必要がなくなります。


MUIのスタイルをレイヤーに入れる

Next.jsのApp Routerでは、2章で置いた AppRouterCacheProvider にオプションを渡します。

<AppRouterCacheProvider options={{ enableCssLayer: true }}>
  {children}
</AppRouterCacheProvider>
tsx

これでMUIが生成するスタイルが @layer mui に入ります。

Next.js以外の構成では、StyledEngineProvider に同じ意味のpropがあります。

<StyledEngineProvider enableCssLayer>
  <ThemeProvider theme={theme}>{children}</ThemeProvider>
</StyledEngineProvider>
tsx
2つの仕組みを共存させるイメージ

Tailwind CSSと組み合わせる

レイヤーに入れただけでは足りません。レイヤーの順番を宣言する必要があります。

Tailwind CSS v4を使う場合、公式ドキュメントが示している順番はこれです。

@layer theme, base, mui, components, utilities;
css

mui が utilities より前にあるのがポイントです。後ろのレイヤーが勝つので、Tailwindのユーティリティクラス(utilities)がMUIのスタイルに勝てるようになります。

この宣言により、!important なしでTailwindのクラスでMUIコンポーネントを上書きできます。

<Button variant="contained" className="rounded-none">
  角丸なしのボタン
</Button>
tsx

レイヤーを細かく分ける

MUIのスタイルを1つの mui レイヤーにまとめるのではなく、役割ごとに分けることもできます。

createTheme({ modularCssLayers: true });
tsx

これを有効にすると、MUIのスタイルが5つのレイヤーに分かれます。

レイヤー中身
mui.globalCssBaseline と GlobalStyles
mui.componentsコンポーネントの基本スタイル
mui.themeテーマによる上書き
mui.customMUI以外の styled() で作ったもの
mui.sxsx propのスタイル

順番の宣言も細かくなります。

@layer theme, base, mui.global, mui.components, mui.theme, mui.custom, mui.sx, components, utilities;
css

「MUIの基本スタイルだけ上書きしたいが、sx で書いた指定は残したい」といった細かい制御が必要になったときの選択肢です。最初から使う必要はありません。


既存プロジェクトに後から入れるとき


そもそも併用すべきか

先生先生

技術的には共存できますが、「ボタンはMUI、カードはTailwind」のように混ぜると、どちらの流儀で書くべきか毎回迷うことになります。

併用がうまくいくのは、役割で線が引けるときです。

線の引き方例
画面で分ける公開ページはTailwind、管理画面はMUI
部品で分ける複雑な挙動を持つ部品(テーブル・ダイアログ)だけMUI
レイアウトと部品で分ける配置はTailwind、フォーム部品はMUI

避けたいのは、同じ1つのボタンをMUIで作ってTailwindで上書きする、という重ね方です。技術的には可能でも、読む人にとっては「どちらを直せばいいのか」が分からなくなります。


よくあるハマりどころ

Tailwindのクラスが効かない

enableCssLayer を有効にしていないか、@layer の順番を宣言していません。両方が必要です。

レイヤーを有効にしたら既存のCSSが効きすぎる

レイヤー外のスタイルが強くなったためです。!important の付いた箇所を優先的に見直します。

sx とTailwindのクラスを同じ要素に書いてしまう

どちらも効きますが、スタイルの出どころが2か所に分かれて追いにくくなります。要素ごとにどちらかへ寄せてください。

CSS Modulesでも同じ問題が起きる

Tailwindに限った話ではありません。CSS Modulesや素のCSSでも、MUIより詳細度が低ければ同じことが起きます。対処も同じです。


ちゃんと使うためのポイント

  • MUIのスタイルは詳細度が高く、外から素直に上書きすると負ける
  • enableCssLayer でMUIを @layer mui に入れ、レイヤーの順番を宣言する
  • Tailwind CSS v4との併用は @layer theme, base, mui, components, utilities;
  • レイヤー外のスタイルはレイヤー内に必ず勝つ。既存プロジェクトでは見た目の変化に注意
  • 細かく制御したいときは modularCssLayers
  • 併用するなら、画面や部品で役割の線を引く

これでこの本は終わりです。基本の sx からテーマ、slot、ダークモード、他のCSSとの共存まで一通り扱いました。個々のコンポーネントの細かいAPIは公式ドキュメントが最も正確なので、そちらを引きながら使ってください。


参考リンク