Edited at

Material-UI と styled-components を組み合わせて、React のサイトを怠惰にスタイリングする。

Redux HooksRedux Starter Kit を組み合わせると、かなり楽に redux が導入できるということを先日、ご紹介いたしました。

ロジックの記述が楽になったので、今度はウェブサイト、ウェブアプリの見た目をらくに記述できるようになりましょう。

ということで、今回はスタイリングのお話です。

紹介するのは React 界隈では有名なライブラリ(だと思っている)、Material-UIstyled components です。

記事の構成は、まずざっと Material-UIstyled components の説明をしてから、両者を組み合わせて利用する方法について紹介します。

結構余分な話も入っていますから、本題を見たい人は以下のリンクから飛んでください。

Material-UI と styled components を一緒に使う


Material-UI

Material-UI は Google が提唱する Material Design に準拠したコンポーネントライブラリです。

material-ui.png

少しのコードで、マテリアルデザインのコンポーネントをサイトに配置できます。

たとえば、クリックしたら波のエフェクトが発生する、ちょっとかっこいいボタンが以下のようなコードで書けます。

<Button variant="contained">ボタンだよ</Button>

<Button variant="contained" color="primary">ボタンだよ</Button>

mui-button.gif

チェックボックスやテキストエリアもかなり凝ったエフェクトなのに簡単に記述できていい感じです。


  • チェックボックス

mui-checkbox.gif

 <Checkbox checked={check} onChange={handleChange} />


  • テキストフィールド

mui-textfield.gif

<TextField label="Name" value={input} onChange={handleChange}/>

input, check は状態の変数、handleChange は状態変化のハンドラーメソッドだと思ってください。

たぶん React の書籍をみたら、利用方法が紹介されていることが多いですし、知っている人も多いかと思います。

ということで、各コンポーネントの説明はそちらに譲ります。

(どこか手頃な記事がどこかに転がってないか軽く調べてみましたけど、見当たりませんね……。Material-UI に関する詳しい使いかたの説明記事欲しい人、言ってくださったら書くかもしれません)


React Hooks を利用したスタイリング - useStyles

Material-UI のコンポーネントだけでもかなり見た目を整えることができますが、微調整するためにはやはり CSS の記述が必要です。

React の CSS の記述方法には、素の CSS、CSS Module、CSS in JS(数多のライブラリ)があります。

Material-UI でも独自の CSS in JS の記述方法が用意されています。

さらに Material-UI は Version 4.0.0 が 2019年 5月 23日にリリースされ、React Hooks の仕組みを取り入れた CSS in JS の記述ができるようになりました。

書籍にはまだ載っていないことでしょうし紹介します。

以下はサンプルコードです。

import React from "react";

import { Paper, makeStyles } from "@material-ui/core";

// スタイルの記述をする
const useStyles = makeStyles(theme => ({
outer: {
width: "100%",
height: "100%",
padding: theme.spacing(4),
backgroundColor: theme.palette.background.default
},

inner: {
width: "720px",
minHeight: "420px",

margin: "0 auto",
padding: theme.spacing(2),

display: "flex",
alignItems: "center",
justifyContent: "center"
}
}));

// クラス
const UseStyleSample = props => {
// useStyles() を呼ぶと上記スタイリングが適応されるユニークなクラスネームが取得できる
const classes = useStyles();

return (
// 各コンポーネントにスタイルをあてる
<div className={classes.outer}>
<Paper className={classes.inner}>
Hooks でクラススタイルが書きやすくなったよ
</Paper>
</div>
);
};

export default UseStyleSample;

今回のコードで生成される UI はこんな感じです。

useStyles.png


解説

簡単な解説です。


makeStyle()

スタイルを定義するメソッドです

makeStyle(theme) => ({

// クラスのスタイル
})

上記のように theme を引数に取る関数を与えてやると、Material-UI の Theme が利用できます。Theme を利用して色指定を行うと、後々サイトのテーマカラーを変更するときなどに一括で指定できて便利です。


useStyles()

スタイルに対応したユニークなクラスネームを取得します。

const classes = useStyles()


各コンポーネントで利用

各コンポーネントではclassName={classes.****(スタイル名)}と指定して、スタイルを適用させます。

<div className={classes.outer}>

// 中身
</div>

このように Hooks を利用して、結構わかりやすくスタイルが記述できます。


useStyles の嬉しいところ


  • CSS in JS のお陰で 1つのファイルにコンポーネントの要素、スタイルを固められる

  • スタイルの指定が v3 までの HOC(withStyles) を利用したときより、書きやすくなった

  • エディターのコード補完も効きやすくなった


useStyles の辛いところ


  • CSS を記述しているのに、maxWidth とスタイルの指定が大文字だったりと、
    オブジェクトスタイル辛い


総括

オブジェクトスタイルで CSS を記述することに我慢すれば、Material-UI は結構使えます。

コンポーネントでボタンやチェックボックス、テキストフィールド、ドロワー……etc など、マテリアルデザインで使えそうなコンポーネントはたいてい用意されていますから。

さあ、いちからコンポーネントを作るなんてことをせず、これで怠惰にサイトデザインしましょう。

……と、ここで終われたら記事書いている私としても楽なんですが(ちょっと疲れてきた)、やっぱり CSS のオブジェクトスタイルが記述しにくいので、別の手段を考えましょう。

だって CSS ジェネレーターで生成した CSS がそのままコピペできないなんて辛いですもん!!(楽したい)


Styled Components

Styled Components は CSS 記法のまま CSS in JS を行うことができるライブラリです。

styled-components.png

サンプルコードはこんな感じ。

import React from "react";

import styled from "styled-components";

// Title コンポーネントの作成、中身は h1 タグにスタイルを適応したもの
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`
;
// Wrapper の作成、中身は section タグにスタイルを適応したもの
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`
;
// 作成した Title と Wrapper はコンポーネントとして利用できる

const HelloWorld = () => (
<Wrapper>
<Title>Hello World!</Title>
</Wrapper>
);

export default HelloWorld;

生成される画面は以下のようなもの

styled-hello.png

styled-components を利用すると、JavaScript でありながら、CSS の記法でスタイルを定義できます。

そしてクラス名の指定をする必要もありませんし、コンポーネント単位でスタリングしやすいです。

ブラボー。

ざっと記事を探してみましたけど、この辺に詳しい説明が載っています。もちろん一番くわしいのは公式サイトですので、そちらを閲覧することが一番良いかとおもいます。

これからのReactコンポーネントのスタイリングはstyled-componentsが良さそう

styled-componentsを使ったCSS設計

なぜ styled components は JavaScript なのに CSS 記法が書けるのでしょうか? それは ECMACScript 6 で利用可能になったテンプレートリテラルを利用しているからです。テンプレートリテラルによって、複数行の文字列が定義できるからです。テンプレートリテラルを知っておくと React のコンポーネントの作成や JS の記述にも役に立つので知識として仕入れておくのもいいと思います。

参考になる記事を貼っておきます。

JavaScript の テンプレートリテラル を極める!

このように styled components を利用することで、スタイリングを簡単に行うことができます。

こいつを使えば、React の CSS in JS で疲労することもないので、これを使っていきましょう。

デザイナーさんが作成した CSS やデザインツールで生成した CSS もそのままコピペできて便利です。

さあこいつで怠惰にスタイリングしましょう。


Material-UI と styled components を一緒に使う

といって、 styled components はかなり便利なのですが、やっぱりいちからコンポーネントを作るのは面倒くさいので、前述の Material-UI と組み合わせてみましょう。

そうです、Material-UI と styled components は一緒に利用できます。少し前までは一緒に利用できなかったり、TypeScript の型定義がぶっ壊れてたりと散々だったんですが、現在のバージョンでは確実に動作します。

Material-UI の公式もそう言っています。


The styled() method works perfectly on all of our components.


訳:styled() メソッドは完璧に私達のコンポーネントで動作するぜ。Style Library Interoperability

使いかたは簡単で、Material-UI のコンポーネントを styled components に食わせてやるだけです。

サンプルコード

import React from 'react';

import styled from 'styled-components';
import { Button } from '@material-ui/core';

// Material-UI をカッコで囲んで、styled の引数にしてやる
const StyledButton = styled(Button)`
background: linear-gradient(45deg, #fe6b8b 30%, #ff8e53 90%);
border-radius: 3px;
border: 0;
color: white;
height: 48px;
padding: 0 30px;
box-shadow: 0 3px 5px 2px rgba(255, 105, 135, .3);
`
;

export default function StyledComponents() {
return (
<div>
<Button>Material-UI</Button>
<StyledButton>Styled Components</StyledButton>
</div>
);
}

表示例

material-styled-button.png

時たま styled-component のスタイリングと Material-UI のスタイリングの適用順序が逆になって、うまいことスタイルがあたらないこと場合もあります。

material-styled-button1.png

そのときは、ウェブサイトのルートコンポーネント付近に Material-UI の StylesProvider を配置し、Material-UI のスタイル順序を制御してやります。injectionFirst のオプションを与えることで、styled components がスタイルを上書きできます。

方法

以下のライブラリをインストールする

# with npm

npm install --save @material-ui/styles

# with yarn
yarn add @material-ui/styles

そして StylesProvider でスタイル適応順序の指定

import { StylesProvider } from '@material-ui/styles';

// ルートコンポーネント付近に配置。App とか Layout コンポーネントの中とか
<StylesProvider injectFirst>
<App />
</StylesProvider>

ということで、Material-UI によってある程度デザインされたコンポーネントを利用しつつ、styled components でお馴染みの CSS を記述できるとお話できました。

ということで、これで怠惰にスタイリングだ。いえーい。


さらに怠惰にスタイリング

このままでも楽なのですが、もう少し楽しましょう。


Material-UI と styled components のテーマの共通化

Material-UI も styled components のどちらもテーマシステムが用意されています。プライマリーカラーやセカンダリーカラーを変更するときは、テーマを弄ります。

テーマのお陰でコンポーネントごとに個別で色指定をする必要もなくなり、怠惰にスタイリングできます。

さらに Material-UI と styled components のテーマを共通化しておくともっと楽になります。

共通化するといっても、難しいことはなく、同じテーマを各テーマプロバイダーに与えてやるだけです。

では、その方法について。

まずは theme オブジェクトを生成します。Material-UI の createMuiTheme を利用すると、Material-UI のデフォルトテーマから拡張できます。(デフォルトのテーマ定義はこちらで確認できます

たとえば、こんな感じのウェブアプリを作成する場合、色のテーマ設定は以下のようになります。

color-image.png

画像はmaterial design paletteより

コード

import { createMuiTheme } from "@material-ui/core";

const theme = createMuiTheme({
palette: {
primary: {
// 緑色
main: "#8BC34A",
dark: "#689F38",
light: "#DCEDC8"
},
secondary: {
// オレンジ
main: "#FF5722"
},
text: {
// ちょっと薄い黒
primary: "#212121",
secondary: "#757575"
}
}
});

export default theme;

createMuiTheme は結構優秀です。palette のなかの secondary には main の色しか指定していません。ですがcreateMuiTheme がよしなに secondary.darksecondary.light の色を生成してくれます。(もちろん、きっちりと個別に色を指定したほうがいいですよ)

さて、theme を作成したので、各テーマプロバイダーに提供してあげます。配置場所はルートコンポーネント付近です。

Material-UI も styled components の両者とも、ThemeProvider という名前なので、as を利用してエイリアスをつけてます。

import React from "react";

import { ThemeProvider as MaterialThemeProvider } from "@material-ui/styles";
import { ThemeProvider as StyledThemeProvider } from "styled-components";
import theme from "./Theme";

const App = props => {
return (
<>
<MaterialThemeProvider theme={theme}>
<StyledThemeProvider theme={theme}>
<>
<header />
<main />
<footer />
</>
</StyledThemeProvider>
</MaterialThemeProvider>
</>
);
};

export default Layout;

これで theme が利用可能になりました。

テーマを利用しながら、styled components でとあるコンポーネントの色を指定する場合、以下のようにします。

import styled from "styled-components"

// props.theme のなかにテーマが格納されている
const StyledDiv = styled.div`
color:
${props => props.theme.palette.primary.main};
`

※補足

TypeScript を利用している場合、さらに設定が必要となります。theme の型が指定されていないためです。

styled.d.ts というファイルを作成し、styled-components の型を拡張します。

src/types/styled.d.ts


// import original module declarations
import "styled-components"
import { Theme } from "@material-ui/core"

// and extend them!
declare module "styled-components" {
export interface DefaultTheme extends Theme {
// 追加でテーマを拡張する場合、この中に定義をかく。
borderRadius: string
}
}

TypeScript のお陰で補完がさらに利くようになって便利です。参考:Create a declarations file

テーマシステムによってさらに怠惰にスタイリングできりようになりました。


styled-components のエディタープラグイン

styled-components ではシンタックスハイライトが動作するように、Atom、Visual Studio Code、WebStorm といった各エディターのプラグインが用意されています。

Syntax Highlighting

こいつを VS Code に入れるとハイライトのほかに、コード補完も効き便利なのでインストールしています。

syntax.gif


復習

長々と説明してきたので復習がてら整理します。

ライブラリのインストール

npm install --save @material-ui/core @material-ui/styles styled-components

今回説明してきたことのソースコード全体。

src/theme.js: theme の作成

import { createMuiTheme } from "@material-ui/core";

const theme = createMuiTheme({
palette: {
primary: {
main: "#8BC34A",
dark: "#689F38",
light: "#DCEDC8"
},
secondary: {
main: "#FF5722"
},
text: {
primary: "#212121",
secondary: "#757575"
}
}
});

export default theme;

src/App.js : ルートコンポーネント付近にテーマプロバイダーを設置

import React from "react";

import {
ThemeProvider as MaterialThemeProvider,
StylesProvider
} from "@material-ui/styles";
import styled, {
ThemeProvider as StyledThemeProvider
} from "styled-components";
import theme from "./theme";

const App = () => (
<StylesProvider injectFirst>
<MaterialThemeProvider theme={theme}>
<StyledThemeProvider theme={theme}>
// ここからメインのコンポーネントを記載する
// <Main></Main> とか
</StyledThemeProvider>
</MaterialThemeProvider>
</StylesProvider>
)

src/components/StyledComponents.js: Material-UI のコンポーネントを styled-components で装飾する。

import React from 'react';

import styled from 'styled-components';
import { Button } from '@material-ui/core';

// Material-UI をカッコで囲んで、styled の引数にしてやる
const StyledButton = styled(Button)`
background: linear-gradient(45deg, #fe6b8b 30%, #ff8e53 90%);
border-radius: 3px;
border: 0;
color: white;
height: 48px;
padding: 0 30px;
box-shadow: 0 3px 5px 2px rgba(255, 105, 135, .3);
`
;

export default function StyledComponents() {
return (
<div>
<Button>Material-UI</Button>
<StyledButton>Styled Components</StyledButton>
</div>
);
}

以上です。これで快適に React のスタイリングができます。


蛇足


その1

ちなみに、蛇足ですが、emotion というスタイリングライブラリも便利です。個人的にはこちらも結構好きです。styled-components の機能も取り入れながら、その他拡張も行ったライブラリです。

この辺がわかりやすいかもです。

CSS-in-JSのライブラリとして「emotion」を選択している理由


その2

Material-UI は v4 になってから、styled-components のように記述するメソッドが追加されました。ですが、若干機能が劣っていて使いにくいです。styled-components もしくは emotion を使ったほうが良いです。


まとめ

Material UI と styled-components についてでした。

これで怠惰にスタイリングしていきましょう。