Material UI (MUI) 入門

フロントエンドのスタイリングについて、考えた時にUIライブラリとして候補に上がってくる`Material UI (MUI)`を触ってみて基礎的な内容と、感じたことをメモで残しました。

## この記事の対象者
- cssの基本的な文法は分かる
- MUIを使ってみようと思っているが、使うイメージが全然わかない人
- MUI の文法の基礎を知りたい人


## 環境
シンプルに `npx create-react-app アプリ名`で生成した状態です。
```package.json
  "dependencies": {
    "@emotion/react": "^11.11.4",
    "@emotion/styled": "^11.11.5",
    "@mui/material": "^5.15.21",
    "react": "^18.3.1",
  }
```


## [Container](https://mui.com/material-ui/react-container/)

コンテンツを水平方向に中央揃えでき、最も基本的なレイアウト要素になる。
デフォルトで左右に`padding`がデフォルトで適用されている

```jsx
<Container>Hello World</Container>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2508607/f2fdbebc-bd89-58de-360d-d0064feadd50.png)

`backgroud-color、height` などを変更したい時は、他のMUIコンポーネントに共通している `sx props` を使用すると良い。

```jsx
<Container sx={{bgcolor: 'cyan', height: '64px'}}>Hello World</Container>
```

## [Box](https://mui.com/material-ui/react-box/)
MUIのテンプレートを見ているとめっちゃ見る要素。
`div`タグに変換されるが、あえて`Box`を使用する理由は `sx props` を使用できることにある

せっかくなので、`sx`も少し深ぼってみる

## [sx](https://mui.com/system/getting-started/the-sx-prop/)
スタイルをシュートハンド記法で書くことができる仕組みを提供してくれている。
```jsx
    <Container
      sx={{
        bgcolor: 'cyan',
        boxShadow: 1,
        borderRadius: 2,
        p: 2,
        minWidth: 300,
      }}
    >Hello World</Container>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2508607/e1466c82-3bb1-56cd-d5fc-46a6267b5548.png)




使用している`bgcolor`などはCSSのプロパティでは `backgroud-color`になるが、これはMUIのショートハンド（省略記法）で、以下のような省略がある。
p: padding
m: margin
py: padding top & bottom

使用可能なプロパティのリストも公式に載っているので、[こちら](https://mui.com/system/getting-started/the-sx-prop/#theme-aware-properties]
)を参考にすると良い

その他、MUIを初めて使ってみて慣れない記法があったので、少しまとめようと思う


### spacingプロパティ
`margin, padding` などの `spacing` プロパティでは `px` を使用するのではなく 1, 2 ... と数値型を指定する。
```jsx
    <Typography sx={{m: 1, bgcolor: 'pink'}}>Hello</Typography>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2508607/309ab469-bcab-f56d-f0ab-f0774f5b826d.png)

1 = 8px と 8px単位で組み込まれていることに注意が必要。
「めんどくさくね？」と感じるかもしれないが、8pxルールが設けられていることでコンテンツの大きさや余白の秩序をより厳密に保ちやすくなるようにも感じる

### hover 
便利だと感じたのはhoverを指定できること
通常、`style` に指定しても `hover` を効かせることはできない
```jsx
  // 以下のホバーは機能しない
  <Button style={{background: 'cyan', ':hover': {background: 'gray'}}}>送信</Button>
```
が、`sx` を使用することで `hover` を適用できる
```jsx
    <Button sx={{bgcolor: 'cyan', ':hover': {bgcolor: 'gray'}}}>送信</Button>
```

そして、これは他の擬似要素（`::befire, ::after` など）でも適用できる。
例えば、 `before, after` 擬似要素にスタイルを当てることもできる。
```jsx
    <Box
      sx={{
        position: 'relative',
        p: 2,
        my: 2,
        "&::before, &::after": {
          content: '""',
          position: 'absolute',
          width: '100%',
          height: '2px',
          bgcolor: 'black',
        },
        "&::before": {
          top: 0,
        },
        "&::after": {
          bottom: 0,
        },
      }}
    >
      <Typography variant="h6" textAlign="center">
        Box with Top and Bottom Lines
      </Typography>
    </Box>
```

以下のように、擬似要素にスタイルを当てて、線を表現することもできる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2508607/8088886f-09fc-4b01-0bb9-9c7551c0bd26.png)


### [ブレイクポイント](https://mui.com/material-ui/customization/breakpoints/)
ブレイクポイントに合わせたスタイルを適用することもできるのでレスポンシブデザインも簡単に設定できる
```jsx
    // xs: 0px
    // sm: 600px
    // md: 900px
    // lg: 1200px
    // XL: 1536px

    <Box
      sx={{
        bgcolor: 'cyan',
        width: {
          xs: 100,
          xl: 1200,
        },
      }}
    >
      <Typography variant="h6" textAlign="center">
        Box with Top and Bottom Lines
      </Typography>
    </Box>
```

また、デフォルトの設定では `xs, lg` とかぱっと見、「どのような条件で値を変化させるのか」のルールが分かりにくい。もっと直感的な命名にしたいこともあるだろう。

`width: { pc: 100, mobile: 1200}`のように PC, Mobile版 とユースケースを明確にしたい。

そんな時はカスタムテーマを設定し、適用したい範囲を `ThemeProvider`でラップすると良い。

```jsx
import * as React from 'react';
import Box from '@mui/material/Box';
import { createTheme, ThemeProvider } from '@mui/material/styles';

const theme = createTheme({
  breakpoints: {
    values: {
      mobile: 0,
      tablet: 640,
      laptop: 1024,
      desktop: 1280,
    },
  },
});

export default function CustomBreakpoints() {
  return (
    <ThemeProvider theme={theme}>
      <Box
        sx={{
          width: {
            mobile: 100,
            laptop: 300,
          },
        }}
      >
        This box has a responsive width
      </Box>
    </ThemeProvider>
  );
}
```

メモ: 
カスタムテーマを各ファイルで呼び出すと冗長なので、`theme`ファイルを作成し、`App.js` あたりで`provide` してしまうのが綺麗なんじゃないかと思う

```theme.jsx
// 略

export const theme = createTheme({
  breakpoints: {
    values: {
      mobile: 0,
      tablet: 640,
      laptop: 1024,
      desktop: 1280,
    },
  },
});
```

```App.jsx
 // 略
 function App() {
  return (
    <ThemeProvider theme={theme}>
      <AppContextProvider>
        // routerとかの定義
      </AppContextProvider>
    </ThemeProvider>
  );
}
 
```




## [Typography](https://mui.com/material-ui/react-typography/)
文字を入力する時に使用する
デフォルトで　`pタグ` として出力されるが、`variant props` を使用することでh1タグなどに変換できる
`variant` の種類も[公式](https://mui.com/material-ui/react-typography/#usage)に載っているので良い感じに使い分けたい。

ちなみに `variant props` は他のコンポーネント（ [Buttonコンポーネント](https://mui.com/material-ui/react-button/#basic-button) とか）にも用意されているので、用途にあった`variant`をチョイスしたい。

```jsx
    <Container sx={{bgcolor: 'cyan', height: '88px'}}>
      <Typography variant='h3'>Hello World</Typography>
      <Typography variant='subtitle1'>good morning!</Typography>
    </Container>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2508607/92343e5f-cfe0-b2df-c481-9025b54f3b60.png)

## 備考
`Container, Box, Typography` とMUIの記法についてざっくり分かるとあとはテンプレートを見ると書いている内容がざっくり理解できる...はず。

基本的にはテンプレートをコピペして、必要な部分のみに削ったり、詳細なプロパティを調整するだけで立派なレイアウトを組み立てることができそうです。

## まとめ
ここまでみていただきありがとうございます。　m(._.)m

MUIを使う上で頻出のコンポーネントについて軽く触れました。
記事には載せてはいませんが、テンプレートを使った上で感じたことは

- **書き方にルールがあるため、スタイリングに規則性が生まれそう**
- **基本テンプレを調整するだけで実装できるのでリードタイムを格段に短縮できそう**

などとメリットも感じますが

- **デフォルトで存在する padding などを調整したい時は上書きで行う必要がある**
- **単位、書き方が特殊なのでキャッチアップにコストがかかる**
- **アプデがあった時の追随が結構しんどいかも**
- **MUIのデザインに引っ張られるので、オーバーライドが多いと実装が大変**

といったデメリットもあり、全部MUIで書くと後々恨まれそうな気がします。

しかし、[カレンダー](https://mui.com/x/react-date-pickers/date-range-picker/) や [chart](https://mui.com/x/react-charts/styling/) など普通に実装するとあまりにめんどくさい機能をサクッと実装できる機能を提供していることもあるので、この恩恵は捨てがたい...

実装がしんどい部分だけをMUIにしてみるなどの手段も候補に入れても良さそうですね。