React + TypeScript: MUIコンポーネントのスタイルをcreateTheme()のstyleOverridesキーでツリーごとに上書きする


MUI(本稿執筆時v5.4.1)のコンポーネントのスタイルは、ただCSSのクラスを割り当てるだけでは適用できないことが少なくありません。作法にしたがったカスタマイズが必要です。いくつかあるやり方の中から、「Global style overrides」で解説されている、コンポーネントツリーごとにスタイルを上書きする手順と注意についてご説明します。つぎのCodeSandboxのコードがButtonAccordionのスタイルをカスタマイズした作例です(2022/02/16にTextFieldを追加)。

React + TypeScript: Customizing style of MUI components

Buttonのスタイルを替える

まずは、前出「Global style overrides」で紹介されているButtonコンポーネントのコード例です。カスタマイズしたいコンポーネントツリーをThemeProviderで包みます(なお、「Theme provider」参照)。

そのthemeプロパティに定めるのが、createTheme()の戻り値です。引数のオプションオブジェクトには、カスタマイズするのがコンポーネントですので、componentsプロパティにコンポーネント名を与えてください。名前(MuiButton)は、APIリファレンスの「Component name」で調べます。スタイルを上書きするキーがstyleOverridesです。CSSルールというのはどこに適用するかのキーワードで、やはりAPIリファレンスの「CSS」でお確かめください。CSSの定義の仕方は、基本的にReactのJSXに定めるstyleと同じです。

つぎのコードで、Buttonコンポーネントのテキストカラーが黒、フォントは少し大きめの1remになりました。

src/App.tsx
import { Button } from '@mui/material';
import { ThemeProvider, createTheme } from '@mui/material/styles';

const theme = createTheme({
    components: {
        // コンポーネント名
        MuiButton: {
            styleOverrides: {
                // CSSルール名
                root: {
                    // CSS定義
                    color: 'black',
                    fontSize: '1rem',
                },
            },
        },
    },
});
function App() {
    return (
        <ThemeProvider theme={theme}>
            <Button>font-size: 1rem</Button>
        </ThemeProvider>
    );
}

export default App;

Accordionのスタイルを替えるときは注意が必要

Buttonコンポーネントのカスタマイズは簡単にできました。厚切りジェイソンなら「OK!パターン見えてきたよ!」と叫ぶところです。ところが、Accordionで「Why MUI!!」という羽目に。

pose_english_why_man.png

「Accordion」の「Basic accordion」のコード例で試します。コンポーネント名はMuiAccordionです。つぎのコードは、追加・修正箇所のみ抜き出しました。たしかにこれで、Accordionコンポーネントのテキストカラーは青くなるのです。ところが、fontSize: '2rem'と加えても大きさが変わりません。フォントがらみの変更はきかないようです。

src/App.tsx
import {
    Accordion,
    AccordionDetails,
    AccordionSummary,
    Typography
} from "@mui/material";
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';

const theme = createTheme({
    components: {
        MuiAccordion: {
            styleOverrides: {
                root: {
                    color: 'blue',
                },
            },
        },
    },
});
function App() {
    return (
        <ThemeProvider theme={theme}>
            <Button>font-size: 1rem</Button>
            <div>
                <Accordion>
                    <AccordionSummary
                        expandIcon={<ExpandMoreIcon />}
                        aria-controls="panel1a-content"
                        id="panel1a-header"
                    >
                        <Typography>Accordion 1</Typography>
                    </AccordionSummary>
                    <AccordionDetails>
                        <Typography>
                            {/* ...[略]... */}
                        </Typography>
                    </AccordionDetails>
                </Accordion>
                {/* ...[略]... */}
            </div>
        </ThemeProvider>
    );
}

改めて確かめると、Accordionは複数のサブパーツから組み立てられています。そこで、カスタマイズの対象をそれらのコンポーネント名に差し替えてみたりもしました。それでも、フォントのスタイルは変わりません。落ち着いてコード例のJSXを見ると、テキストはTypographyコンポーネントに差し込まれています。コンポーネント名をMuiTypographyに書き替えたらビンゴでした(結果は、冒頭のCodeSandbox作例でお確かめください)。

src/App.tsx
const theme = createTheme({
    components: {
        MuiTypography: {
            styleOverrides: {
                root: {
                    color: 'blue',
                    fontSize: '2rem',
                },
            },
        },
    },
});

MUIコンポーネントのスタイルには、親から子に受け継がれるものもあります。けれど、子コンポーネント独自に保持する場合もあるというのが教訓です。だからといって、JSXの子コンポーネントひとつずつにstyleプロパティを加えようとしたら、煩雑で管理しにくくなります。コンポーネントツリーをThemeProviderで包み、themeで特定のコンポーネントを狙い撃ちして定めるのはスマートでしょう。ただ、仕組みや確かめるべきことの要領がつかめないと、とまどってしまいます。お気をつけください。

[2022/02/16以下追記]

TextField上部に縮小表示されるlabelのフォントサイズを大きくしたい

TextFieldコンポーネントにlabelプロパティを定めると、デフォルトではテキストがまずプレースホルダーのようにフィールド内に示され、フォーカスを入れると上部に縮小して表示されます。これがどうも小さく感じられてなりません。日本語は画数が多いからというのも理由です。「憂鬱な薔薇」などと与えた日には、とくに中高年にはつらいものがあるでしょう。

そこで、テキストフィード上部に縮小表示されたラベルのフォントサイズを大きくしたいというのがお題です。ラベルのコンポーネント名はMuiInputLabelだとわかりました。すると、前述のふたつのコンポーネントと同じく、styleOverridesキーでthemeを上書きすればたしかにフォントサイズは大きくなります。けれど、それではプレースホルダーのテキストも変わってしまうのです。

縮小表示されたラベルだけスタイルを変えたいという場合には、CSSのルールからshrinkを選んでください。

src/App.tsx
const theme = createTheme({
    components: {
        MuiInputLabel: {
            styleOverrides: {
                shrink: {
                    fontSize: '1.1rem'
                }
            }
        }
    }
});