[React + Typescript] emotion の Theming 機能を使って複数のTheme切り替えを実装してみた

React + Typescript 製アプリの Theme を独自定義したものに色々切り替えられるようにしてみました。
2021.04.25

やりたいこと

emotionThemeProvider を使って自前で定義した複数の Theme の切り替えを実装してみました。

コードの量が多かったため全体のコードは CodeSandbox で公開しています。

emotion とは

glam, glamor, styled-component, glamorous といった既存のライブラリから影響を受けて作られた CSS in JS ライブラリです。

書き方もstyled-componentによく似ており、styled-component でできることは emotion でも大体できそうという印象を受けました。

新規プロジェクトの作成

React + typescript の雛形を作成します。

npx create-react-app emotion_sample --template typescript

emotion の導入

プロジェクトのルートディレクトリへ移動し、以下のモジュールをインストールします。 必要なのは@emotion/react@emotion/styledの2つです。

cd emotion_sample
yarn add -D @emotion/react @emotion/styled

Theme を定義する

theme/theme.tsに Theme を複数定義します。

theme/theme.ts
export const pink = {
  fonts: {
    mainFont: "Avenir Next, sans-serif",
    subFont: "Hira Kaku Pro N, sans-serif",
    textFont: "Hiragino Sans",
    numberFont: "Avenir Next, sans-serif",
  },
  colors: {
    mainColor: "#64363C",
    subTextColor: "#8E354A",
    lineColor: "#E6E5E4",
    backgroundColor: "#F8C3CD",
    titleBackgroudColor: "#F8F4F4",
    accentColor: "#EB7A77",
    stageColor: "#CB1B45",
  },
};

export const yellow = {
  fonts: {
    mainFont: "Times New Roman",
    subFont: "Garamond",
    textFont: "Tahoma",
    numberFont: "Times New Roman",
  },
  colors: {
    mainColor: "#5B622E",
    subTextColor: "#8D742A",
    lineColor: "#E6E5E4",
    backgroundColor: "#DCB879",
    titleBackgroudColor: "#EFBB24",
    accentColor: "#212121",
    stageColor: "#90B44B",
  },
};

export const sky = {
  fonts: {
    mainFont: "Impact",
    subFont: "Tahoma, sans-serif",
    textFont: "Impact",
    numberFont: "Impact",
  },
  colors: {
    mainColor: "#6c756b",
    subTextColor: "#8D8D8D",
    lineColor: "#E6E5E4",
    backgroundColor: "#F3F4F5",
    titleBackgroudColor: "#96C5F7",
    accentColor: "#212121",
    stageColor: "#4D565D",
  },
};

emotion Theme の型を Override する

src/下に@typesフォルダを切り、先ほど定義した Theme の型を@types/theme.d.tsファイルに定義して emotion の Theme 型を拡張します。

@types/theme.d.ts
import '@emotion/react'

declare module '@emotion/react' {
  interface Theme {
    fonts: Fonts
    colors: Colors
  }
}

interface Colors {
  mainColor: string
  subTextColor: string
  lineColor: string
  backgroundColor: string
  titleBackgroudColor: string
  accentColor: string
  stageColor: string
}

interface Fonts {
  mainFont: string
  subFont: string
  textFont: string
  numberFont: string
}

これで IDE 上での型補完が有効になります。

ThemeProvider を追加する

Theme を反映するためにApp.tsxThemeProviderを追加します。

App.tsx
import React from "react";
import {pink} from "./themes/theme";
import {ThemeProvider} from "@emotion/react";

function App() {
  return (
    <ThemeProvider theme={pink}>
      <div className="App">Hoge</div>
    </ThemeProvider>
  );
}

export default App;

Component で要素を参照する

ここまでで Theme の設定が完了したのであとはコンポーネント内で Theme の値を参照してください。

const H1 = styled.h1`
  font-weight: 500;
  font-family: ${theme.fonts.mainFont};
  font-size: 28px;
  text-align: center;
  color: ${theme.colors.accentColor};
  margin-top: 8px;
  margin-bottom: 0;
`;

あとがき

今回初めて CSS in JS ライブラリを利用してみましたが要素の Styling においては特に嵌ることもなくすでにある CSS の知識だけで書き進めることができました。emotionの機能や書き方などの詳細についてはしばらく使ってみて知見がたまったらまたまとめようと思います。

参考