TypeScriptでMUIをカスタマイズする方法を詳しく知りたい
公式にもカスタマイズする方法が載っているのですが初心者には説明が足りないと感じます
そこでReact+TypeScript環境でMUI(Material-UI)をカスタマイズする方法を詳しく解説します
- Visual Studio Code:1.76.0
- Node.js:8.15.0
ReactプロジェクトはCreate React Appで作成しており、TypeScriptテンプレートを使用しています
詳しくはこちらの記事を参照してください
テーマ
MUI(Material-UI)にはデフォルトテーマが設定されています
そのテーマをカスタマイズするためにはThemeProviderを使用します
ThemeProviderだけでもカスタマイズできるのですが、CssBaselineをラップしないとBody要素にテーマが適用されません
CssBaselineはリセットCSSと呼ばれており、デフォルトスタイルはブラウザごとにバグや微妙な差があり、この差を調整してくれるのがリセットCSSなのです
まずはデフォルトで設定されているダークモードに変更してみます
function App() {
const theme = createTheme({
palette: {
mode: 'dark',
},
});
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<FormControlLabel control={<Checkbox />} label="Label" />
<Button color="primary" variant="contained">
primary
</Button>
</ThemeProvider>
);
}
試しにCssBaselineを外してみると、配下のCheckboxとButtonには適用されるのですが、Bodyには適用されないのが分かります
スタイル付きコンポーネント
styled()を使用することでスタイル付きのカスタムコンポーネントを作成することもできます
次の例ではCheckboxコンポーネントに対して基本色とchecked状態の色を変更しています
const StyledCheckbox = styled(Checkbox)(({ theme }) => ({
color: orange[500],
'&.Mui-checked': {
color: orange[500],
},
}));
function CustomCheckbox(props: CheckboxProps) {
return <StyledCheckbox {...props} />;
}
MUIではパレットカラーとして赤やオレンジなど19種類が用意されています
500という数字は色相と色合いを表した値で50が最も明るく、900が最も暗い色合いになります
- &.Mui-checkedの&とは?
-
&(アンパサンド)とは、SASS(.scss)における親要素(セレクタ)自体を意味します。&.Mui-checkedがCSSにコンパイルされると.css-1hwodmi-MuiButtonBase-root-MuiCheckbox-root.Mui-checkedのようになります
- {…props}の…とは?
-
…変数はJavascriptのスプレッド構文になります。ここでは引数で渡されたpropsのプロパティを展開してStyledCheckboxのプロパティに設定しています
モジュールの拡張
モジュールを拡張する方法を解説します
Color
Buttonの色にはprimaryを指定していますが、primary以外にもsecondary、error、warning、info、successがあります
ですが、実際に開発していくとこれ以外のカラーを使用したくなる場合があります
TypeScriptを使用している場合、モジュールの拡張をしないと次のようにエラーになります
今回はモジュール拡張をするのに型定義ファイル (.d.ts)を使用する方法を紹介します
- 型定義ファイルとは?
-
JavaScriptのライブラリをTypeScriptなどから利用する場合、TypeScriptは静的型付け言語なので、型の情報が必要になります。そのような場合、JavaScriptのオブジェクトをdeclareを使ったアンビエント宣言することで、型の情報を明示的にすることができます。アンビエント宣言は専用の型定義ファイル( .d.ts )で管理することが推奨されています
独自のカラー「neutral」を使えるように型定義ファイルを作成します
declare module '@mui/material/styles' {
interface Palette {
neutral: Palette['primary'];
}
interface PaletteOptions {
neutral?: PaletteOptions['primary'];
}
}
declare module '@mui/material/Button' {
interface ButtonPropsColorOverrides {
neutral: true;
}
}
export {};
MUIのすべてではないかもしれませんが、PaletteとPaletteOptionsのようにXXXとXXXOptionsというセットの組み合わせになっているのでカスタマイズする場合は両方の変更が必要になります
次にcreateThemeでneutralの色を設定します
const theme = createTheme({
palette: {
neutral: {
main: '#64748B',
contrastText: '#fff',
},
},
});
Buttonのprimaryをneutralに変更すると次のように表示されます
Typography
文字のサイズや装飾などの文字スタイルを変更する場合にはTypographyを使用します
Typographyはさまざまなコンポーネントで継承されているため、Typographyの文字サイズを変更すると他のコンポーネントにも影響します
次はTypographyコンポーネントを使用していないのにCheckboxとButtonのfontsizeが変更された例です
const theme = createTheme({
typography: {
fontSize: 8,
},
});
Typographyはデフォルトだとpタグになります
Boxと同様に他のコンポーネントにもなることでき、バリエーションにはh1~h6、MUI独自のsubtitle1など13種類あります
このバリエーションに新しくposterが使えるように型定義ファイルを変更します
declare module '@mui/material/styles' {
interface TypographyVariants {
poster: React.CSSProperties;
}
interface TypographyVariantsOptions {
poster?: React.CSSProperties;
}
}
declare module '@mui/material/Typography' {
interface TypographyPropsVariantOverrides {
poster: true;
}
}
export {};
次にcreateThemeでposterの文字スタイルを設定します
const theme = createTheme({
typography: {
poster: {
fontSize: '4rem',
color: 'red',
},
},
});
Typographyコンポーネントのvariantでposterを指定すると次のように表示されます
まとめ
今回作成したソースは次の通りです
App.tsx
import Button from '@mui/material/Button';
import CssBaseline from '@mui/material/CssBaseline';
import FormControlLabel from '@mui/material/FormControlLabel';
import Typography from '@mui/material/Typography';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import * as React from 'react';
import CustomCheckbox from './components/CustomCheckbox';
function App() {
const theme = createTheme({
palette: {
neutral: {
main: '#64748B',
contrastText: '#fff',
},
},
typography: {
poster: {
fontSize: '4rem',
color: 'red',
},
},
});
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<FormControlLabel control={<CustomCheckbox />} label="Label" />
<Button color="neutral" variant="contained">
neutral
</Button>
<Typography variant="poster">poster</Typography>
</ThemeProvider>
);
}
export default App;
CustomCheckbox.tsx
import Checkbox, { CheckboxProps } from '@mui/material/Checkbox';
import { orange } from '@mui/material/colors';
import { styled } from '@mui/material/styles';
import * as React from 'react';
const StyledCheckbox = styled(Checkbox)(({ theme }) => ({
color: orange[500],
'&.Mui-checked': {
color: orange[500],
},
}));
function CustomCheckbox(props: CheckboxProps) {
return <StyledCheckbox {...props} />;
}
export default CustomCheckbox;
global.d.ts
declare module '@mui/material/styles' {
interface Palette {
neutral: Palette['primary'];
}
interface PaletteOptions {
neutral?: PaletteOptions['primary'];
}
interface TypographyVariants {
poster: React.CSSProperties;
}
interface TypographyVariantsOptions {
poster?: React.CSSProperties;
}
}
declare module '@mui/material/Button' {
interface ButtonPropsColorOverrides {
neutral: true;
}
}
declare module '@mui/material/Typography' {
interface TypographyPropsVariantOverrides {
poster: true;
}
}
export {};