LoginSignup
33
27

More than 1 year has passed since last update.

Material UI v4のスタイル指定(JSS)

Last updated at Posted at 2020-03-19

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のインラインスタイル属性ではなく、クラス名による指定の仕組みである。

大きな流れ

以下のような流れとなる。

  1. スタイルシートの元になる「元のスタイルシートオブジェクト」をJSで作る const styles = { root: { backgroundColor: 'red', }, };
  2. 「元のスタイルシートオブジェクト」を元に、Material UIが準備しているHoCやhooksを使って、以下を生成する。
  • 「元のスタイルシートオブジェクト」のプロパティ名(「論理クラス名」とでも呼ぶとする)と、コンポーネントローカルにリネームされた実際に生成される「クラス名」(「物理クラス名」とでも呼ぶとする)の対応表。こんなやつ:
    {"tabBar":"TabBar-tabBar-284","selectedTab":"TabBar-selectedTab-285"}
    "tabBar"が論理クラス名、"TabBar-tabBar-284"が物理クラス名と本文書では呼ぶとする。物理クラス名をプログラマが意識することは(デバッグ時を除き)基本的にはない。
    * この物理クラス名でのCSSスタイル定義が、背後で<head>に挿入される(プログラマは意識しないでよい)。
  1. 「論理クラス名→物理クラス名の対応表」を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コンポーネントが内部的に持っていて使用している「元のスタイルシートオブジェクト」のカスタマイズのための「置き換え対応表」として使用される。

33
27
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
27