水戸地図(β)

2021年09月20日

MUI (Material-UI) v5 のスタイリング方法

2021年9月16日に MUI (Material-UI) v5 が正式にリリースされました Material-UI v4 のリリース が 2019年3月なので、約2年半ぶりのメジャーアップデートです。 正式名称が Material-UI から MUI に変更になり 、パッケージ名も @material-ui/* から @mui/* になりました。

MUI v5 ではコンポーネントのスタイリングの実装方法や記法が一新し、Material-UI v4 以前までの独特な記法から、 Theme UI chakra などで用いられている sx Prop によるスタイリングになりました。
大幅な変更になるので、v4 からの移行コストは大きいですが、新たに MUI を学ぶ人には優しくなった印象です。

MUI v5 のスタイリング方法を v4 の記法と比較しながら紹介していきます。

yarn add @mui/material @emotion/react @emotion/styled

スタイリングの実装方法

MUI v5 では、スタイリングのコアライブラリが JSS から emotion に変わりました。この記事では説明しないので、詳しく知りたい方は Introducing MUI Core v5.0 を参照してください。

さて、v4 では useStyles という自作フックでコンポーネントのスタイリングをしていましたが、この変更によって v5 からはコンポーネントに sx Prop で直接スタイルを書くことができます。

v4 と v5 の記法の違い

Material-UI v4 の記法

makeStyles でスタイル用のフックを作成し、コンポーネント内で className を取得します。

import { makeStyles, createStyles } from "@material-ui/core/styles";

// フックを作成
const useStyles = makeStyles((theme) =>
  createStyles({
    root: {
      color: theme.palette.primary.main,
      padding: theme.spacing(2),
    },
  }),
);

function Hoge() {
  // フックで className を取得
  const classes = useStyles();
  return <div className={classes.root}>Hoge</div>;
}

MUI v5 の記法

sx Prop にスタイルを直接記入します。

import Box from "@mui/material/Box";

function Hoge() {
  return <Box sx={{ color: "primary.main", p: 2 }}>Hoge</Box>;
}

sx Prop によって記述量がとても少なくなり、初心者にもわかりやすい記法になりました。
color に指定した primary.main は、MUI テーマのパレットを利用する方法です。 v5 で新たに導入されたこの記法 では、theme を引数に取る関数(後述)を指定しなくても MUI テーマのパレットを利用することができます。

MUI System
mui.com
Palette
mui.com
広告

sx Prop について

MUI v5 のすべてのコンポーネントは sx Prop でスタイリング可能です。sx Prop は従来の(React では非推奨ですが)style Prop に似ていますが、 MUI テーマに基づいたスタイリング や、 レスポンシブで動的なスタイリング が容易という特徴があります。

<Box
  sx={{
    width: {
      xs: 100, // theme.breakpoints.up('xs')
      sm: 200, // theme.breakpoints.up('sm')
      md: 300, // theme.breakpoints.up('md')
      lg: 400, // theme.breakpoints.up('lg')
      xl: 500, // theme.breakpoints.up('xl')
    },
    height: (theme) => theme.spacing(10), // => 80px (8px * 10)
  }}
>
  This box has a responsive width.
</Box>

sx Prop では、v4 の makeStyle によるスタイリングと同様に、値にtheme を引数にとった関数を指定することができます。

<Box> コンポーネントについて

<Box> コンポーネント は、sx Prop のプロパティを Prop として直接記述できる基礎的なコンポーネントです。実は v4 時代には既に導入されていました が、パフォーマンスが低いという難点がありました。リリースノートによると、v5 では大幅にパフォーマンスが改善されたそうです。

import Box from "@mui/material/Box";

// padding: 16px (spacing(8px) * 2)
// background-color: theme.palette.primary.dark
<Box p={2} bgcolor="primary.dark" width={100} height={100}>
  hoge
</Box>;

<Box> コンポーネントと <Typography> コンポーネントではこの方法で、 sx Prop を使わずにスタイルを設定することができます。

Properties
mui.com

一般の HTML 要素のスタイリング

<div><span> などの一般の HTML 要素は sx Prop を持ちません。これらをスタイリングするには、<Box> コンポーネント や <Typography> コンポーネントを使い、component Prop で HTML 要素を指定するといいでしょう。

import Box from "@mui/material/Box";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";

interface ItemProps {
  children: React.ReactNode;
}

function Item({ children }: ItemProps) {
  return (
    <Typography
      variant="body2"
      component="span"
      sx={{ backgroundColor: "secondary.light", p: 1 }}
    >
      {children}
    </Typography>
  );
}

function Hoge() {
  return (
    // py (padding-top, padding-bottom): { xs: 16px, sm: 8px }
    <Box component="nav" sx={{ py: [2, 1] }}>
      <Stack spacing={2} direction={{ xs: "column", sm: "row" }}>
        <Item>Top</Item>
        <Item>News</Item>
        <Item>Site Map</Item>
      </Stack>
    </Box>
  );
}

margin, padding と spacing という単位

MUI にはスタイリングの統一感を出すために、padding や margin に指定するための spacing という単位が存在します。spacing は MUI テーマ で規定されていて、デフォルトでは 8px に設定されています。

sx Prop や <Box> コンポーネントで設定する padding や margin には、spacing が単位として使われています。v4 では makeStyles でフックを作る際に、theme.spacing を使うことで spacing の単位が明示的に使われていましたが、v5 になって spacing という単位が暗黙的に使われるようになったので注意が必要です。

// margin-right: 8px (8px * 1)
<Typography component="span" sx={{ mr: 1 }}></Typography>

// padding-top, paading-bottom: 16px (8px * 2)
<Box py={2}></Box>

また、padding と margin だけは spacing を単位に取りますが、その他のプロパティは基本的に px を単位に取るので混同しないように注意が必要です。また、borderRadius は独自の theme.shape.borderRadius (デフォルトは spacing の半分である 4px)という単位を取るので注意しましょう。

// 正円になる
<Box sx={{
  display: 'flex',
  width: 64, // => 64px
  height: 64, // => 64px
  borderRadius: 8, // => 32px (4px * 8)
}} />

<Box sx={{
  display: 'flex',
  width: (theme) => theme.spacing(8), // 64px
  height: (theme) => theme.spacing(8), // 64px
  borderRadius: 8, // 32px (4px * 8)
}} />
Spacing
mui.com

感想

v3 以前の withStyles、v4 の makeStyles といった Material-UI 特有の記法は Material-UI 入門者にはやや難解な印象がありました。
v5 では sx Prop で記述する形になったので、初心者にはわかりやすくなったものの、パレットカラーの記法や、padding, margin における spacing の暗黙的な利用など、覚えなくてはいけない事柄は増えた印象です。とはいえ、この辺りはおそらく Theme UI Tailwind CSS でも同じことでしょう。

MUI v5 のスタイリングの触りの部分だけをざっと説明しました。
この先は 公式ドキュメント 、特に MUI System の項を熟読することをお勧めします。

参考

雨のぼり / セカイイチ (2005)

広告

2021年09月20日

MUI (Material-UI) v5 のスタイリング方法

技術記事

Top

水戸地図(β)