React with TypeScript プロジェクトの Material-UI でデザインをカスタマイズする
前回は Material-UI のコンポーネントを利用するところまでやりました。今回はその続きになります。ソースコードも前回の記事を踏まえていますのでその点ご了承ください。(といって、踏まえないでも読めてしまうとは思いますが。)
さて、今回あ Theme をいじったり Style をいじったりします。その方法は色々あるのですが、今回はその一例となります。なるべく今現在の主流と思われるやり方、といいますか、公式で使われている方法を採用しています。
シリーズ一覧
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 はこちらです。
Palette について
では、 Theme の色は何になっているのでしょうか? これは Palette というもので定義されています。 Primary
, Secondary
, Error
, Warning
, Info
, Success
という区分けでそれぞれ色が定義されています。
AppBar
に色を指定しないと Primary
になります。 Primary
はデフォルトで青色です。こういう理屈で青色のバーが上部に表示されていました。
公式サイトの Palette はこちらです。
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 の追加です。
AppBar
の position
と color
は AppBar
固有の属性です。公式のサイトにも利用方法が書かれています。
Link
の className
は 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
属性に指定ができます。
makeStyles
に独自 CSS を記述する。makeStyles
の結果(=useStyles
)を React のコンポーネント内で受け取る。- タグの中の
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()
については公式サイトの以下に説明があります。
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 を編集する話をしていきたいと思います。