Material UIでのスタイル指定方法を説明します。Material UIのスタイルシステムの実体はJSSです。themeについては言及しません。もっと基礎的なところだけです。
注: 本記事の内容はMaterila UI v4に限定です。Material UI v5(MUI)はデフォルトでemotionベースになったので記述が異なります。
classNameで即値指定
HTMLで言うclass=の指定。Material UI的には何もしない。BEMやscssでもなんでもいいのですが、外部スタイルシートを定義してあてがっていくパターン。
<Hoge className="hoge fuga"/>
styleで指定
styleプロパティの指定。Material UI的には何もしない。オブジェクトがdomのstyle属性に変換されて付けられる、Reactの仕組み。
<Hoge style={{fontSize:1, padding: '1rem'} />
DOM上は、要素に直接紐付いたスタイル指定(devtool上、element.styleで表示される)となる。疑似要素は原理的に指定不可。
StyledComponent💅で指定。
こちらを参照。本資料では説明を割愛する。
JSSのスタイルシートオブジェクトで指定
Material UIがかかわるのはここから。JSSは基本的にDOMのインラインスタイル属性ではなく、クラス名による指定の仕組みである。
大きな流れ
以下のような流れとなる。
- スタイルシートの元になる「元のスタイルシートオブジェクト」をJSで作る
const styles = { root: { backgroundColor: 'red', }, };
- 「元のスタイルシートオブジェクト」を元に、Material UIが準備しているHoCやhooksを使って、以下を生成する。
- 「元のスタイルシートオブジェクト」のプロパティ名(「論理クラス名」とでも呼ぶとする)と、コンポーネントローカルにリネームされた実際に生成される「クラス名」(「物理クラス名」とでも呼ぶとする)の対応表。こんなやつ:
{"tabBar":"TabBar-tabBar-284","selectedTab":"TabBar-selectedTab-285"}
"tabBar"が論理クラス名、"TabBar-tabBar-284"が物理クラス名と本文書では呼ぶとする。物理クラス名をプログラマが意識することは(デバッグ時を除き)基本的にはない。
* この物理クラス名でのCSSスタイル定義が、背後で<head>に挿入される(プログラマは意識しないでよい)。
- 「論理クラス名→物理クラス名の対応表」をMaterial UIが提供するReactコンポーネントにおいて以下のいずれかの方法で利用する
- 対応表から物理クラス名を取得して「classNames」propsで指定する。
- 対応表から物理クラス名を取得して「classes」propsで指定する。
「元のスタイルシートオブジェクト」の作りかた
単なるJSオブジェクトとして「スタイルシートオブジェクト」を作る
const styles = {
root: {
backgroundColor: 'red',
},
};
もしくは
const styles = createStyles({
root: {
backgroundColor: 'red',
},
});
両者の意味は同じである。後者ではTypeScriptの型エラーを黙らすことができる。
themeを引数とする関数として「スタイルシートオブジェクト」を作る
const styles = (theme: Theme) =>{
root: {
backgroundColor: theme.color.red,
},
};
もしくは
const styles = (theme: Theme) => createStyles({
root: {
backgroundColor: theme.color.red,
},
});
両者の意味は同じである。後者ではTypeScriptの型エラーを黙らすことができる。
ここでは、Reactのインラインstyle属性のようにJavaScriptオブジェクトを用いるが、rootの階層が入っていることが異なる。この階層の名前は、(実CSSクラス名を隠蔽した)、ローカルなCSSクラス名と思ってよい。
CSSクラスなので疑似クラスも指定できる。たとえば、以下のように:hover疑似クラスが指定できる。(JSSの機能)
const styles: any = (theme: Theme): StyleRules =>
createStyles({
button: {
margin: '0.3rem',
'&:hover': { transform: 'scale(1.1)' },
},
「元のスタイルシートオブジェクト」から「クラス名の対応表」を入手する
React Hooks系で
こちらから引用だが、以下のように「makeStyles」の引数に「元のスタイルシートオブジェクト」を渡すと、「クラス名の対応表」を取得できるフック関数を入手することができる。そのフック関数の呼び出し結果をclasses変数に取得している。(createStylesは使用していない。)
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
const useStyles = makeStyles({
root: {
backgroundColor: 'red',
color: props => props.color,
},
});
export default function MyComponent(props) {
const classes = useStyles();
return <div className={classes.root} />;
}
HoC系で
HoCであるwithStylesを使う。こちらも引用だが以下となる。意味はHooks系と同じだが、「元のスタイルシートオブジェクト」をwithStylesの入力として与え、「クラス名の対応表」がprops.classesに得られるようなコンポーネントを生成している。
import React from 'react';
import { withStyles } from '@material-ui/core/styles';
const styles = {
root: {
backgroundColor: 'red',
},
};
function MyComponent(props) {
return <div className={props.classes.root} />;
}
export default withStyles(styles)(MyComponent);
取得した「クラス名の対応表」の使いかた
「クラス名の対応表」は、入手方法としてHooksでもHoCで得ることができるが、同じものである。上記サンプルでは、入手した「クラス名の対応表」はclassesという名前のprops(props.classes)もしくは変数classesに格納している。
「クラス名の対応表」には2つの使いかたがある。
classNameに与える
サンプルにもあるが、
return <div className={classes.root} />;
のように指定する。複数あれば
return <div className={classNames(classes.root,classes.hoge)} />;
のようにNPMモジュールclassnamesを使うのがよいだろう。classnamesはクラス名を結合させているだけである。(clsxというのもあり効率がよいらしい)。
classesで指定
最後になったが、これがJSSの本領である。Material UIのコンポーネントにはCSS APIというものが定義されており、それを使ってカスタマイズすることができる。
たとえば、Tabsコンポーネントには以下のようにかかれている。
CSS
- Style sheet name: MuiTab.
- Style sheet details:
Rule name | Global class | Description |
---|---|---|
root | .MuiTab-root | Styles applied to the root element. |
labelIcon | .MuiTab-labelIcon | Styles applied to the root element if both icon and label are provided. |
textColorInherit | .MuiTab-textColorInherit | Styles applied to the root element if the parent Tabs has textColor="inherit". |
textColorPrimary | .MuiTab-textColorPrimary | Styles applied to the root element if the parent Tabs has textColor="primary". |
textColorSecondary | .MuiTab-textColorSecondary | Styles applied to the root element if the parent Tabs has textColor="secondary". |
selected | .Mui-selected | Pseudo-class applied to the root element if selected={true} (controlled by the Tabs component). |
これらを必要に応じてオーバーライド定義することができる。赤字"selected"は後述の説明で例として使用するCSS APIのキー名である。このスタイルをオーバーライドする方法は3つある。
- With a rule name of the classes object prop.(classes propsのルール名)
- With a global class name. (グローバルクラス名)
- With a theme and an overrides property..(テーマと上書きプロパティ)
以降では最初の「 rule name of the classes object prop.」について説明する。
やるべきことは、classes属性に、指定したCSS API名をキー(ここではslected)とし「クラス名の対応表」をひっぱって物理クラス名を指定するのである。
const styles = (theme: any) =>
createStyles({
selectedTab: {
backgroundColor: '#eeeeff',
fontWeight: 'bold',
},
});
:
<Tabs>
<Tab
classes={{ selected: classes.selectedTab }} />
:
</Tabs>
するともともとMUIで定義されていたCSSに上書きされる形でCSSスタイルが定義される。
MUIコンポーネントのclasses属性は、それぞれのMUIコンポーネントが内部的に持っていて使用している「元のスタイルシートオブジェクト」のカスタマイズのための「置き換え対応表」として使用される。