14
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Chakra UI : ダークモードの切り替えをアイコンを使って実装する

Last updated at Posted at 2021-08-24

使用環境

MacOS BigSur(11.5.2) / VScode(1.59.1) / Node.js (16.1.1) / "@chakra-ui/icons": "^1.0.15" / "@chakra-ui/react": "^1.6.6"

はじめに

最近、Next.js(React)を使ったプロジェクトでChakra UIを使用しています。
Chakra UIに関する記事がQiitaではあまり見られないので、学びになったことをちょこちょこ投稿していくことにしました。

今回は、タイトルの通りChakra UIを使ったダークモードの実装方法について書いていきます。
驚くほど簡単だったので、Reactの基礎が分かっている人なら10分以内で実装できてしまうと思います。

では早速、やっていきましょう。

実装

まず初めに基本の流れだけざっと説明しておくとこんな感じになります

  1. 準備
     - Chakra UIのインポート
     - Providerのセットアップ
  2. useColorModeのセットアップ
  3. コンポーネントで使用する(以上)

+α ダークモード/適用時のスタイルをグローバルで指定する方法
+α 初期設定をする方法(CRA /Next.jsの場合)


1. 準備

・Chakra UIのインストール
導入していない方は、まずはここから始めましょう。既に終わっている方はスキップして2から始めてください。

#npmの場合
npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4
#yarnの場合
yarn add @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4

・Providerのセットアップ

Create React Appの場合
Rootとなっているコンポーネントファイルで以下のように設定していきます。

import * as React from "react"
// 1. import `ChakraProvider` component
import { ChakraProvider } from "@chakra-ui/react"
function App({ Component }) {
// 2. Use at the root of your app
return (
  <ChakraProvider>
    <Component />
  </ChakraProvider>
 )
}

Next.jsの場合
pages/_app.js か pages/_app.tsx(TypeScriptを導入している際はこちら)で以下のように設定していきます。

import { ChakraProvider } from "@chakra-ui/react"
function MyApp({ Component, pageProps }) {
  return (
    <ChakraProvider>
      <Component {...pageProps} />
    </ChakraProvider>
  )
}
export default MyApp

2. useColorModeのセットアップ

ダークモード実装を簡単にできるuseColorMode というHooksと、アイコンに使用する IconButtonMoonIconSunIcon をインポートする。(お好みに応じてreact-iconsを使用するのもOK)

import { IconButton, useColorMode } from '@chakra-ui/react'
import { MoonIcon, SunIcon } from '@chakra-ui/icons'

あとは、useColorModeをこの様に定義する。このuseColorModeというのは現在のカラーモードの状態をcolorModeで保持して、toggleColorModeで切り替えもしてくれる機能があります。

const { colorMode, toggleColorMode } = useColorMode()

3. コンポーネントで使用する

あとはインポートしたIcconButtonというコンポーネントに記述していきます。ポイントはiconプロパティを使用していることと、その中でcolorModeが'light' かどうかで、<MoonIcon /><FaSun /> を出し分けしている点です。

ちなみにcolorModeは、デフォルトで'light''dark' というstringのみを保持する様になっています。(color-mode.utils.d.ts内での宣言: export declare type ColorMode = "light" | "dark";

<IconButton
// _focus={{_focus: "none"}} //周りの青いアウトラインが気になる場合に消す方法
mb={10}
aria-label="DarkMode Switch"
icon={colorMode === 'light' ? <MoonIcon /> : <FaSun />} //自分の好みでSunアイコンはreact-iconsを使用しています
onClick={toggleColorMode}
/>

ezgif-2-889e090ad641.gif

この画像のように、Chakra UIで提供しているほとんどのコンポーネントはデフォルトでダークモードに対応してcolorを変化してくれます。

By default, most of Chakra's components are dark mode compatible. In some scenario, you might need to make your component respond to color mode.

もし自分で切り替え時の色を設定したい場合は上記に併せて useColorModeValueが使えます。(以下は[公式サイト][2]から引っ張ってきたコードです。)

useColorModeValueは2つの引数を取ることができ、1つ目の引数が'light' に対応し、2つ目の引数が'dark' に対応します。

import { useColorModeValue, useColorMode } from '@chakra-ui/react'

function StyleColorMode() {
  const { toggleColorMode } = useColorMode()

  const bg = useColorModeValue("red.500", "red.200")
  const color = useColorModeValue("white", "gray.800")

  return (
    <>
      <Box mb={4} bg={bg} color={color}>
        This box's style will change based on the color mode.
      </Box>
      <Button size="sm" onClick={toggleColorMode}>
        Toggle Mode
      </Button>
    </>
  )
}
    

こんなふうになります。
ezgif-2-d6c7fe21b154.gif

ちなみに自分で実装してみた場合はこんな感じです。

import { useColorModeValue, useColorMode } from '@chakra-ui/react'

const { colorMode, toggleColorMode } = useColorMode()
const bgGradient = useColorModeValue("linear(to-l, blue.400, #7928CA,#FF0080)", "linear(to-r, blue.400, #7928CA,#FF0080)"

<Heading
 mb={8}
 bgClip="text"
 bgGradient={bgGradient}
 fontSize="6xl"
 fontWeight="extrabold"
>
 NEXT TODO
</Heading>     
    

どうでしょうか?密かにタイトルのグラデーション色が反転してますね。
(うん、わかりづらいですね。)
ezgif-2-9fee03cffd46.gif

+α グローバルでダークモード/ライトモード時のスタイルを指定する

グローバル適用するにはCustomize themeが使えるんでしたね。ここではCustomize themeについての詳しい説明は省きます。気になる方は[公式Doc][3]みてみてください。
ポイントはpropsとして別のコンポーネントで定義したcolorModeを受け取って、colorを変化させている点です。

import { extendTheme } from '@chakra-ui/react'

// NB: Chakra gives you access to `colorMode` and `theme` in `props`
export const theme = extendTheme({
styles: {
  global: (props) => ({
    'html, body': {
      fontSize: 'sm',
      color: props.colorMode === 'dark' ? 'white' : 'gray.600',
      lineHeight: 'tall',
      padding: 0,
      margin: 0,
    },
    a: {
      color: props.colorMode === 'dark' ? 'white' : 'gray.600',
      textDecoration: 'none',
    },
  }),
}
}

rootとなるコンポーネントで導入した <ChakraProvider>にthemeをインポートしてpropsとして渡すのをお忘れなく。

import { ChakraProvider } from '@chakra-ui/react'
import { theme } from 'src/theme/theme'

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ChakraProvider theme={theme}>
      <Component {...pageProps} />
    </ChakraProvider>
  )
}
export default MyApp

+α 初期設定をする方法(CRA /Next.jsの場合)

先程のthemeにconfigでinitialColorModeuseSystemColorMode を定義しextendThemeに渡します。

// theme.js

import { extendTheme } from "@chakra-ui/react"

const config = {
  initialColorMode: "light", // ColorModeの初期値を設定
  useSystemColorMode: false, // システムで導入しているカラーモードを適用するかどうか(falseは適用しない場合)
}

const theme = extendTheme({ config })

export default theme

あとは全体にこの設定を読み込ませていきます。

・Create React Appの場合

themeをインポートしてColorModeScript をindex.jsファイルに記述します。

// index.js

import ReactDOM from "react-dom"
import App from "./App"
import theme from "./theme"

ReactDOM.render(
  <>
    <ColorModeScript initialColorMode={theme.config.initialColorMode} />
    <App />
  </>,
  document.getElementById("root"),
)

・Next.jsの場合

themeをインポートしてColorModeScript を_document.jsファイルに記述します。

import { ColorModeScript } from "@chakra-ui/react"
import NextDocument, { Html, Head, Main, NextScript } from "next/document"
import bodyTheme from "src/theme/theme"

export default class Document extends NextDocument {

  render() {
    return (
      <Html lang="en">
        <Head />
        <body>
          <ColorModeScript initialColorMode={bodyTheme.config.initialColorMode} />
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

最後に

こんなに簡単にダークモードを実装できるなんて思ってもいなかったです。
画面を見る時間が増えている現代ではダークモード導入の必要性も高まっているので、今後も活用していきます。
また、実装していたらChakra UIがなくともReactのHooksで実装できそうだなと思ったので、時間がある時にアレンジを加えて試してみたいと思います。

p.s.
最後の初期設定のやり方自体は理解できたのですが、どうしても自分のプロジェクトにinitialColorModeを導入することができませんでした。systemColorMode はtrueにした際に適用されたのでこちらはうまくできているみたいなので、ColorMode Scriptでの読み込みがうまくできてないのかなと疑って、Customize Themeでbodyにあてていた設定を消したり、色々試したりましたがダメみたいでした。もし理由がわかる方がいたら教えていただきたいです。:bow:

ソースコード: https://github.com/hirooutdoor/next-todo2
・src/theme/theme.ts
・pages/_document.js


参考資料

Chakra UI公式ドキュメント [useColorMode][1]
[1]:https://chakra-ui.com/docs/features/color-mode#usecolormode
[2]:https://chakra-ui.com/docs/features/color-mode#usecolormodevalue
[3]:https://chakra-ui.com/docs/theming/customize-theme

14
8
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
14
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?