Multi Vitamin & Mineral

Multi Vitamin & Mineral です。プログラムに関することを書いております。

React with TypeScript プロジェクトの Material-UI でデザインをカスタマイズする

前回は Material-UI のコンポーネントを利用するところまでやりました。今回はその続きになります。ソースコードも前回の記事を踏まえていますのでその点ご了承ください。(といって、踏まえないでも読めてしまうとは思いますが。)

multimineral-tech.com

さて、今回あ Theme をいじったり Style をいじったりします。その方法は色々あるのですが、今回はその一例となります。なるべく今現在の主流と思われるやり方、といいますか、公式で使われている方法を採用しています。

シリーズ一覧

  1. React + TypeScript の環境構築
  2. Material-UI の導入
  3. Material-UI のデザインをカスタマイズ
  4. Material-UI のレイアウトを作成

Theme という概念

説明をするその前に、 Material-UI には Theme という概念があります。(「Theming」と呼ばれているようです。)

よくあるテーマってやつだね、となんとなく察するかも知れませんが、そうです。 Material-UI のテーマとはどんなんであるのか、今回の前提知識として少し説明をしておきます。

  • Theme により色や配置などのデザインが決まる。
  • 何もしなくてもデフォルトの Theme が適用される。
  • 色は Palette に定義されている。
  • Palette には Primary , Error , Warning などのキーワードがあり、それぞれに色が定義されている。
  • 色を指定するときは、このキーワードを利用する。
  • デザインの変更は、基本はこの Theme や Palette を変更する

混乱しやすい部分は、何もしなくてもデフォルトの Theme が適用される点かなと思います。なので、 Theme を使ってなくても Theme を意識する必要があるので注意です。(本格的に利用するなら Theme をいじらないことってないんでしょうけど、利用しはじめの時はピンとこないかな、と思います。)

Theme と Palette については以上ですが、少しだけ説明を加えておきます。

Theme について

前回の記事では、 AppBar というコンポーネントを使ったら何も設定していないのに「青色」になってました。

デザインをカスタマイズ前
デザインをカスタマイズ前

Theme では「色や配置」などのデザインが定義されています。 AppBar のような Material-UI のコンポーネントには、 Theme で定義された色やデザインになる仕組みになっています。 AppBar が「青色」になっていたのは、 Theme の色が適用されていた訳です。

Theme はカスタマイズが可能です。ですが、特に何もしなかった場合はデフォルトの Theme が適用されます。先程の「青色」はデフォルトの色だった訳です。

公式サイトの Theme はこちらです。

material-ui.com

Palette について

では、 Theme の色は何になっているのでしょうか? これは Palette というもので定義されています。 Primary , Secondary , Error , Warning , Info , Success という区分けでそれぞれ色が定義されています。

AppBar に色を指定しないと Primary になります。 Primary はデフォルトで青色です。こういう理屈で青色のバーが上部に表示されていました。

公式サイトの Palette はこちらです。

material-ui.com

makeStyles で Style を追加する

さて、CSS でデザインをしていきましょう。

/src/templates/DashboardTemplate.tsx を変更します。内容の全貌は以下の通りです。

// /src/templates/DashboardTemplate.tsx
import React from 'react';
import { Link } from 'react-router-dom';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Container from '@material-ui/core/Container';
// (2) 追加
import {
  makeStyles,
  Theme,
} from '@material-ui/core/styles';

// (2) useStyles を追加
const useStyles = makeStyles(
  (theme: Theme) => ({
    toolbar: {
      fontWeight: 'bold',
    },
    link: {
      margin: theme.spacing(1, 1.5),
    },
  })
);

export interface DashboardTemplateProps {
  children: React.ReactNode;
  title: string;
}

const DashboardTemplate: React.FC<DashboardTemplateProps> = ({
  children,
  title,
}) => {
  // (2) classes を追加
  const classes = useStyles();

  return (
    <div>
      {/* (1) それぞれに属性を追加 */}
      <AppBar position="static" color="default">
        <Toolbar className={classes.toolbar}>
          <nav>
            <Link to="/" className={classes.link}>
              Top
            </Link>
            <Link to="/about" className={classes.link}>
              About
            </Link>
          </nav>
        </Toolbar>
      </AppBar>
      <Container>
        <h1>{title}</h1>
        <div>{children}</div>
      </Container>
    </div>
  );
};

export default DashboardTemplate;

(1) 属性の追加

AppBar 以下のタグに属性を追加しています。

      {/* (1) それぞれに属性を追加 */}
      <AppBar position="static" color="default">
        <Toolbar className={classes.toolbar}>
          <nav>
            <Link to="/" className={classes.link}>
              Top
            </Link>
            <Link to="/about" className={classes.link}>
              About
            </Link>
          </nav>
        </Toolbar>
      </AppBar>

それぞれの設定内容について説明します。

  • AppBar
    • position="static" : 表示位置設定です。 CSS の position と同じモノが属性として設定できます。デフォルトは fixed ですので static に変更しています。
    • color="default" : 色です。デフォルトは Theme の Primary が設定されます。 default を指定するとグレー表示になります。
  • Toolbar
    • className={classes.toolbar} : 後述する独自 CSS の追加です。
  • Link
    • className={classes.link} : 後述する独自 CSS の追加です。

AppBarpositioncolorAppBar 固有の属性です。公式のサイトにも利用方法が書かれています。

LinkclassName は React の属性で HTML の class に相当します。 Material-UI では属性値の {classes.toolbar} の方が重要で、次項ではコレについて説明します。

(2) 独自 CSS の追加

makeStyles という機能で独自 CSS を追加しています。この部分に関して話をします。

// (2) useStyles を追加
const useStyles = makeStyles(
  (theme: Theme) => ({
    toolbar: {
      fontWeight: 'bold',
    },
    link: {
      margin: theme.spacing(1, 1.5),
    },
  })
);

// ...中略...

const DashboardTemplate: React.FC<DashboardTemplateProps> = ({
  children,
  title,
}) => {
  // (2) classes を追加
  const classes = useStyles();
  // ...中略...
};

const useStyles = makeStyles(() => ({ ... });useStyles という関数を作っています。 makeStyles は引数に関数を取ります。この関数の返却値( ({ ... }) の部分にあたる)に JSON 形式で CSS を記述します。

できた useStyles は React のコンポーネント内で利用できます。 const classes = useStyles(); のようにして受け取り、 <Link to="/" className={classes.link}> のように className 属性に指定ができます。

  1. makeStyles に独自 CSS を記述する。
  2. makeStyles の結果(= useStyles)を React のコンポーネント内で受け取る。
  3. タグの中の className 属性の値で利用する。

独自 CSS の追加と利用

makeStyles(() => ( ... ));... の部分に JSON で書くだけです。属性は CSS のようなハイフンつなぎではなくキャメルケースになることに注意です。以下のように定義します。

const useStyles = makeStyles(
  () => ({
    toolbar: {
      fontWeight: 'bold',
    },
  })
);

利用も難しくはないと思います。

  const classes = useStyles();
  return (
    <Toolbar className={classes.toolbar}>
  );

こんな感じで toolbar で定義した fontWeight: 'bold' が適用されます。

Theme の値を利用した CSS の追加

先程は 'bold' という属性値を追加しました。この属性値の設定に Theme の値を使うことも可能です。例えば、 border の色に Theme の Primary の色を使いたい、みたいなことです。

今回は、リンク文字のスペースに theme.spacing() という関数を使います。

Theme の spacing

Theme には spacing という値が定義されています。余白に利用する基本的な数字です。デフォルトの Theme だと 8px になっているようです。

リンク文字の余白として、 CSS の margin を追加したいと思います。 theme.spacing() という関数を theme.spacing(1, 1.5), のよう使うと以下のような意味になります。

margin: 8px, 12px;

8px * 1, 8px * 1.5 が展開されて、上記のような CSS として扱われます。(ちなみに、 theme.spacing(1, 2, 3, 4), なら 8px, 16px, 24px, 32px; になります。)

theme.spacing() については公式サイトの以下に説明があります。

material-ui.com

theme.spacing を使った CSS の追加と利用

Theme を使うときは makeStyles の引数が必要になります。 theme という引数(型は Theme なので theme: Theme と書いている)を指定すると、現在適用される Theme を利用されることになります。 theme をどこかで定義する必要はありません。

const useStyles = makeStyles(
  (theme: Theme) => ({
    link: {
      margin: theme.spacing(1, 1.5),
    },
  })
);

利用は難しくはないと思います。

  const classes = useStyles();
  return (
    <Link to="/about" className={classes.link}>
      About
    </Link>
  );

こんな感じで link で定義した margin: theme.spacing(1, 1.5), が適用されます。最終的に margin: 8px, 12px; という CSS として出力されます。

対応結果

以上にて、以下のような表示に変わりました。

デザインをカスタマイズ後
デザインをカスタマイズ後

次は Theme を編集する話をしていきたいと思います。