mcpField
W
web-ui-mui

web-ui-mui

github.com/agents-incデザイン・UI2636 回インストール2 日前に更新
リポジトリ

web-ui-mui について

web-ui-mui は、MUI を使う React 開発者向けのパターンガイドスキルです。テーマ、コンポーネントのスタイル、レイアウト、ダークモード、MUI X を扱い、カスタマイズ方法の選び方に加え、移行や性能、よくある問題も解説します。

主な機能

  1. 01テーマと ThemeProvider の設定
  2. 02sx、styled、テーマ上書きの使い分け
  3. 03slots と slotProps の利用
  4. 04カラースキームとダークモードの実装
  5. 05レイアウトと MUI X のパターン活用

活用シーン

  1. 01React アプリで一貫した MUI テーマを構築
  2. 02コンポーネントのカスタマイズ方法を選択
  3. 03サーバーサイドレンダリングを考慮したダークモードの実装
  4. 04DataGrid、日付ピッカー、チャートの画面開発

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/agents-inc/skills --skill web-ui-mui

インストール後、Claude Code で/web-ui-muiを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

metadata.yaml
reference.md
examples/
core.md
data-grid.md
feedback.md
form-inputs.md
layout.md
navigation.md
styling.md

詳細

MUI(Material UI)のパターン

クイックガイド: MUI は、テーマを基盤とするスタイル設定済みの React コンポーネントライブラリです。createTheme と ThemeProvider で各コンポーネントが参照するトークンを定義します。sx は個々のインスタンスにスタイルを設定し、styled() は再利用可能なコンポーネントを作成します。theme.components はすべてのインスタンスを変更し、slots / slotProps から 内部要素にアクセスできます。現行:v7.x(2025 年 3 月) — CSS レイヤー、slots / slotProps の全体での標準化、 Grid v2 の Grid への昇格、React 19 への対応。MUI X v8 は DataGrid、DatePicker、 Charts をカバーします。スタイリングエンジンには Emotion を使用します。Pigment CSS はまだアルファ版で、開発が保留されています。

詳細なリソース:

  • examples/core.md — カラースキームを使ったテーマ、コンポーネントのオーバーライド、ダークモードメニュー、TypeScript の型拡張、CSS レイヤーを使った SSR および SPA のセットアップ
  • examples/styling.md — styled()、shouldForwardProp によるカスタムプロパティ、テーマ対応の sx、レスポンシブ形式とコールバック形式
  • examples/form-inputs.md — TextField、Select、Autocomplete、slots、slotProps
  • examples/layout.md — Grid、Stack、Box、Container、レスポンシブなカードグリッド
  • examples/navigation.md — AppBar、Drawer、Tabs、レスポンシブなダッシュボードシェル
  • examples/feedback.md — Dialog、Snackbar、Alert、Skeleton、CircularProgress
  • examples/data-grid.md — DataGrid の列、ページネーション、セルのレンダリング
  • reference.md — コンポーネント一覧、テーマの構造、sx の省略形、ブレークポイント、パッケージ一覧、v6 から v7 への移行、アンチパターンのコード

<critical_requirements>

MUI コードを書く前に

アプリを createTheme() インスタンスで ThemeProvider でラップしてください。 そうしないと、コンポーネントは MUI のデフォルトテーマにフォールバックし、正しくレンダリングされるものの、意図とは異なる見た目になります。エラーは出ず、他の人のデザインが適用されるだけです。

バレルではなくパスからインポートしてください — { Button } from "@mui/material" ではなく、@mui/material/Button を使います。バレルを使うと、初回読み込み時に開発サーバーがパッケージ全体を解析します。また、@mui/icons-material だけでもモジュール数は 2000 を超えます。

内部要素にアクセスするには slots と slotProps を使ってください。 これらはすべてのコンポーネントで使える v7 の API です。components と componentsProps は非推奨で、削除が予定されています。

ダークモードの分岐は theme.applyStyles("dark", { … }) として記述してください。 これにより両方のルールセットが出力され、CSS が適切なものを選択するため、サーバーとクライアントで表示が一致します。theme.palette.mode === "dark" はレンダリング時に判定するため、ハイドレーション前に誤ったテーマが一瞬表示されます。

</critical_requirements>


自動検出: MUI、Material UI、@mui/material、@mui/system、@mui/icons-material、 @mui/x-data-grid、@mui/x-date-pickers、createTheme、ThemeProvider、CssBaseline、sx prop、styled、 useTheme、useColorScheme、colorSchemes、cssVariables、applyStyles、slots、slotProps、 shouldForwardProp、GridColDef、StyledEngineProvider、enableCssLayer

対象範囲:

  • MUI のコンポーネントセットを基盤にし、フォークするのではなくテーマを通じてカスタマイズする
  • 特定のオーバーライドに対して、sx、styled()、theme.components、slots/slotProps のどれを使うか選ぶ
  • CSS 変数を使ったサーバーレンダリングに耐える方式を含む、カラースキームとダークモード
  • モジュール拡張を通じたカスタムパレットカラーとタイポグラフィバリアントの型付け
  • MUI X — DataGrid、日付ピッカー、チャート

別の箇所で扱う内容:

  • MUI 独自のテーマを超える CSS の方法論とデザイントークンのアーキテクチャ — enableCssLayer は MUI のスタイルを名前付きレイヤーに配置し、外部スタイルシートが優先順位を設定できるようにします。そのスタイルシートの記述方法は別の箇所で扱います
  • スタイルなしのプリミティブ — このライブラリには完全にスタイルが適用されているため、すべてのピクセルを自分で管理したいプロジェクトには、ヘッドレスプリミティブが適しています
  • フォームの状態とバリデーション — TextField は error と helperText を受け取り、エラーを表示します。また、inputRef を使って、ref を登録するライブラリに基になる <input> を渡せます。これらの値を何で決定するかは、このスキルの対象外です
  • サーバー状態 — コンポーネントはデータを props として受け取ります

テーマがプロダクトです。 パレット、タイポグラフィ、間隔、シャドウ、ブレークポイント、z-index、トランジションはすべて一つのオブジェクトにまとまっており、すべてのコンポーネントがそこから値を読み取ります。呼び出し箇所で記述したオーバーライドは、繰り返し適用する必要がある判断です。同じ判断を theme.components に記述すれば、一度で済みます。

カスタマイズは階層化されており、どの階層を選ぶかが重要です。 このインスタンスだけなら sx、再利用可能なバリアントなら styled()、すべてのインスタンスなら theme.components、自分では所有していないコンポーネント内部の要素なら slots/slotProps を使います。状況に必要な範囲を超えて重い階層を選ぶことが、後から変更できないスタイルの主な原因です。

MUI コンポーネントはクライアントコンポーネントです。 コンテキストとエフェクトを使用するため、サーバーレンダリングを行うアプリでは、ThemeProvider の上にキャッシュプロバイダーを配置し、コンポーネントを使うページをクライアント境界で囲む必要があります。


<decision_framework>

スタイリング階層の選択

Used in one place                  → sx prop
Reused, same shape each time       → styled()
Every instance in the app          → theme.components.MuiX
An element inside the component    → slots / slotProps

レイアウトコンポーネントの選択

One axis, even spacing             → Stack
Twelve-column grid                 → Grid (size prop)
Centred page with a max width      → Container
Anything else that needs sx        → Box

カラースキーム

No dark mode                       → one palette in createTheme
Follow the system only             → cssVariables: true
User can choose                    → colorSchemes + useColorScheme
Both, without a flash              → cssVariables: { colorSchemeSelector: "data" } + useColorScheme

MUI X のデータ表示

Under ~100 rows, read-only         → Table
Sorting, filtering, pagination     → DataGrid
Very large datasets                → DataGridPro (row virtualisation)
Date or time entry                 → DatePicker / DateTimePicker
Charts                             → MUI X Charts
Hierarchy                          → TreeView

</decision_framework>


基本パターン

パターン 1:テーマとプロバイダー
const theme = createTheme({
  palette: { primary: { main: "#1976d2" } },
  typography: {
    fontFamily: '"Inter", sans-serif',
    button: { textTransform: "none" },
  },
  shape: { borderRadius: 8 },
  spacing: 8,
});

<ThemeProvider theme={theme}>
  <CssBaseline />
  {children}
</ThemeProvider>;

CssBaseline は MUI のノーマライズと、テーマの背景色およびテキスト色を <body> に適用します。これがないと、コンポーネントの周囲のページにはブラウザーのデフォルトスタイルが残ります。

コード全文:examples/core.md


### パターン 2:CSS 変数とダークモード
const theme = createTheme({
  cssVariables: { colorSchemeSelector: "data" },
  colorSchemes: {
    light: { palette: { primary: { main: "#1976d2" } } },
    dark: { palette: { primary: { main: "#90caf9" } } },
  },
});

const { mode, setMode } = useColorScheme(); // "light" | "dark" | "system"

cssVariables を使うと、両方のパレットがカスタムプロパティとして出力され、data-* 属性によって切り替えられるため、React の実行前に配色が決まります。初回のレンダーでは mode は undefined です。配色に依存するものをレンダーする前に、値が設定されているか確認してください。

コード全体:examples/core.md


パターン 3:sx プロパティ
<Box
  sx={{
    p: 3, // theme.spacing(3)
    bgcolor: "background.paper", // theme.palette.background.paper
    borderRadius: 1, // theme.shape.borderRadius
    boxShadow: 3, // theme.shadows[3]
    width: { xs: "100%", md: "50%" },
    "&:hover": { boxShadow: 6 },
  }}
/>

数値はテーマのスケールを通して処理され、ドット区切りの文字列はパレットを参照するため、テーマの値をリテラルとして繰り返し記述する必要はありません。ブレークポイントオブジェクトを使えばメディアクエリーを置き換えられ、sx オブジェクトの配列は左から右にマージされます。これにより、オブジェクトを作り直さずに条件付きスタイルを追加できます。

コード全体:examples/styling.md


パターン 4:styled()
const StyledCard = styled(Card)(({ theme }) => ({
  padding: theme.spacing(3),
  "&:hover": { boxShadow: theme.shadows[8] },
  ...theme.applyStyles("dark", { backgroundColor: theme.palette.grey[900] }),
}));

同じスタイルを複数回使う場合に使用します。第 2 引数には shouldForwardProp を指定でき、これによってスタイル設定専用のプロパティが不明な属性として DOM に渡されるのを防ぎます。

コード全体:examples/styling.md


パターン 5:スロットと slotProps
<Autocomplete
  slots={{ paper: CustomPaper }}
  slotProps={{
    paper: { elevation: 8, sx: { borderRadius: 2 } },
    listbox: { sx: { maxHeight: 300 } },
    input: ({ open }) => ({
      sx: { borderColor: open ? "primary.main" : "divider" },
    }),
  }}
  renderInput={(params) => <TextField {...params} label="Framework" />}
/>

slots は内部コンポーネントを置き換え、slotProps はそのコンポーネントを設定します。コールバックとして記述した slotProp には、そのスロット固有の状態が渡されます。スロットのコンポーネントはレンダーの外で定義してください。インラインのアロー関数は毎回新しいコンポーネント型になるため、レンダーのたびにスロットが再マウントされます。

コード全体:examples/form-inputs.md


パターン 6:レイアウト
<Grid container spacing={3}>
  <Grid size={{ xs: 12, md: 4 }}><Sidebar /></Grid>
  <Grid size={{ xs: 12, md: 8 }}><MainContent /></Grid>
</Grid>

<Stack direction={{ xs: "column", sm: "row" }} spacing={2}>
  <Button variant="contained">Save</Button>
</Stack>

<Container maxWidth="lg" sx={{ py: 4 }}>{children}</Container>

v7 では Grid は旧 Grid2 であり、xs/sm/md の個別のプロパティではなく、1 つの size プロパティを受け取ります。以前のコンポーネントも GridLegacy として引き続き使用できます。

コード全体:examples/layout.md


パターン 7:TypeScript の型拡張
declare module "@mui/material/styles" {
  interface Palette {
    neutral: Palette["primary"];
  }
  interface PaletteOptions {
    neutral?: PaletteOptions["primary"];
  }
}
declare module "@mui/material/Button" {
  interface ButtonPropsColorOverrides {
    neutral: true;
  }
}

型拡張は 2 つ必要で、どちらも欠かせません。1 つ目はテーマにトークンを追加し、2 つ目はコンポーネントのプロパティ値としてそのトークンを受け入れられるようにします。2 つ目を省くと、テーマにトークンが含まれていても、すべての呼び出し箇所で color="neutral" が型エラーになります。

コード全体:examples/core.md


パターン 8:サーバーレンダリングの設定
<AppRouterCacheProvider options={{ enableCssLayer: true }}>
  <GlobalStyles styles="@layer theme, base, mui, components, utilities;" />
  <ThemeProvider theme={theme}>
    <CssBaseline />
    {children}
  </ThemeProvider>
</AppRouterCacheProvider>

Emotion はコンポーネントのレンダー時にスタイルを生成するため、サーバーレンダリングを行うアプリでは ThemeProvider の上位にキャッシュプロバイダーが必要です。キャッシュプロバイダーはスタイルを収集し、ストリーミング HTML とともに出力します。これがないと、スタイルのないマークアップが届き、ハイドレーション時にスタイルが再適用されます。MUI は、サポート対象の各サーバーフレームワーク用にアダプターパッケージを公開しています。クライアントのみのアプリでは、代わりに同じ位置で StyledEngineProvider を使用します。enableCssLayer は MUI の出力を名前付きレイヤーに配置し、@layer 宣言によって、そのレイヤーとページ上のほかのすべての要素との順序を固定します。

両方の構成のコード全体:examples/core.md


パフォーマンス

パス指定のインポート。 import Button from "@mui/material/Button" を使うと、バレルの解析を省略できます。バレル形式では、開発サーバーの起動に数倍の時間がかかります。一部のバンドラーやフレームワークはバレルインポートを書き換えるため、コストが発生すると決めつける前に確認してください。パターン ^@mui/[^/]+$ を制限する lint ルールを設けると、再び使われるのを防げます。

コンポーネントが同一性の判定に使う値には、安定した参照を使用してください。 DataGrid の columns、スロットコンポーネント、Dialog の TransitionProps は、いずれも参照で比較されます。モジュールスコープで定義するか、メモ化してください。インラインで定義すると、親がレンダーされるたびに再レンダーまたは再マウントが発生します。

createTheme はバンドルサイズに影響しません。これはコンポーネントではなくデータです。バンドルサイズは、どのコンポーネントをインポートするかで決まります。


<red_flags>

注意すべき問題

実行時に問題が発生します:

  • スタイル関数内の theme.palette.mode === "dark" — レンダリング時に解決されるため、サーバーとクライアントの最初の描画で不一致が起き、誤ったテーマが一瞬表示されます
  • slots 内のインラインアロー関数 — レンダリングごとに新しいコンポーネント型が作られるため、スロットが再マウントされ、状態とフォーカスが失われます
  • DataGrid のインライン columns — レンダリングごとに新しい配列が作られ、グリッド全体が再レンダリングされます
  • @mui/material/styles/createTheme のように2階層より深いパスからのインポート — v7 の ESM パッケージレイアウトでは、これらのパスは公開されていません
  • Alert、Skeleton、Autocomplete に対する @mui/lab からのインポート — v7 ではこれらは @mui/material に移行しました
  • クライアント境界を設けずにサーバー上で MUI コンポーネントをレンダリングすること — コンテキストとエフェクトが必要なため、境界はルートレイアウトだけでなくページに設ける必要があります

意外な動作:

  • spacing はピクセル値ではなく倍率です。デフォルトの基準値は8なので、spacing(2) は16pxになります
  • sx の配列は左から右へマージされ、後の要素が優先されます。これにより条件付きスタイルが機能します
  • TextField は3つのコンポーネント(入力欄、ラベル、ヘルパーテキスト)が組み合わさっているため、実際の <input> を対象にするには slotProps.input を使います
  • zIndex を手動で設定すると、モーダル、ドロワー、スナックバー、ツールチップにすでに値が割り当てられている MUI 独自のスケールと競合します
  • useMediaQuery はサーバーでのレンダリング中に false を返すため、デスクトップ優先の分岐ではハイドレーション前に誤った内容がレンダリングされます
  • useColorScheme().mode は最初のレンダリングでは undefined なので、これを使ってスキームに依存するアイコンをレンダリングすると、ハイドレーション不一致が発生します
  • カスタムのパレットカラーに対する TypeScript 拡張がない場合、型エラーはテーマではなく使用箇所で発生します
  • components と componentsProps は v7 でも引き続き動作しますが、非推奨です

コメント