ChakraUIについてまとめてみた（概要編）

現在携わっているプロジェクトでは、UIライブラリとしてChakraUIを導入しています。
実装を進めるうちに便利だな〜と思う部分がたくさんあったので、今回はChakraUIの概要について共有したいと思います。

## 対象読者
ChakraUIの導入を検討している人・ChakraUIに興味がある人

## そもそも ChakraUIとは？
“Chakra UI”は、UIコンポーネントライブラリの一つです。これを使用することで、1からCSSを記述することなく**スタイルに一貫性を持たせたUI**を楽に実装することができます。

>Our goal is to design simple, composable components that cater to real-life UI design problems. In order to do that, we developed a set of principles that help us always be on that path.
>
>私たちの目標は、実際のUIデザインの問題に対応するシンプルで構成可能なコンポーネントを設計することです。それを行うために、私たちは常にその道を歩むのに役立つ一連の原則を開発しました。

（[公式ドキュメント](https://chakra-ui.com/docs/principles)より引用）

またTailwind CSSと同様にUtility firstの一面も持っており、コンポーネント内をシンプルに保ちつつ、クラス名のネーミングという苦行からも解放されます。

>**Simplicity:**
>Strive to keep the component API fairly simple and show real world scenarios of using the component.
>
>**シンプルさ：**
>コンポーネントAPIをかなりシンプルに保ち、コンポーネントを使用する実際のシナリオを示すように努めます。

>**Naming Props:** 
>We all know naming is the hardest thing in this industry. Generally, ensure a prop name is indicative of what it does. Boolean props should be named using auxiliary verbs such as `does`, `has`, `is` and `should`. For example, `Button` uses `isDisabled`, `isLoading`, etc.
>
>**ネーミングProps：**
>ネーミングがこの業界で最も難しいことであることは誰もが知っています。一般的に、propsの名前がそれが何をするかを示していることを確認してください。Boolean propsでは、`does`や`has`,`is`などの助動詞を使用して名前を付ける必要があります。たとえば`Button`では`isDisable`,`isLoading`を使用します。

（[公式ドキュメント](https://chakra-ui.com/docs/principles)より引用）

ChakraUIの良い点は、上記のようなUtility firstとしてのメリットを享受つつ、UIライブラリとしてしっかりとしたUIを提供してくれるところにあると思います。

Tailwind CSSの場合はUIライブラリとしての機能は比較的薄く、Utility firstの考え方で１からスタイルを作り上げる必要があるので、ChakraUIよりも学習コストも初期実装工数もかかります。（[TailwindUI](https://tailwindui.com/)のようなUIカタログを使えば解決する点かもしれません）

逆を言えば、ChakraUIはTailwindよりもデザインのカスタマイズに多少なりとも制限がかかってくるので、一長一短です。

仮にどちらかを選ぶ必要がある状況になった時は、『独自のデザインを多く適用する必要があるか否か』を基準に考えると良いのかもしれません。

### Pure Typescript である

ChakraUIの良い点の一つとして、Reactでアプリを構築する前提で作られているため、**typescriptで型保護されている** という点も挙げられるかと思います。

スタイルを指定していくときなどもサジェストを出してくれるので、とても開発者体験が良いです！

例えば `Button`にスタイルをつける場合、propsに記述してオーバーライドさせていくことができるのですが

![20220217-210718.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2385943/a352c2e7-ca7a-56b7-06d0-2e745457b608.png)

こんな感じで、`Button`に使用できるものをサジェストしてくれます。

使用できないものが指定されていた場合もtscでエラーを出してくれるので、非常に素早く確実に実装していくことができます。


💡 余談：ちなみに[vue用のchakraUIライブラリ](https://vue.chakra-ui.com/)も存在します。（まだバージョンは `v0.10.*` ）


## 使用例

では、実際にどのように使用するかについてです。
※ 具体的なChakraUI導入の方法は今回は割愛します（詳しくは[公式サイト](https://chakra-ui.com/guides/with-nextjs)を参照）

基本的にはimportしたChakraUIのモジュールを使用し、すでにスタイルがついているコンポーネントを適用していくイメージです。

例えば`spinner` というモジュールを使用すれば、ChakraUIのデフォルトスタイルが当たったスピナーを表示できます。必要であればpropsでスタイルを上書きしていく形になります。

```tsx
// Boxモジュールをインポート
import { Spinner } from '@chakra-ui/react'

...

<Spinner
  thickness='4px'
  speed='0.65s'
  emptyColor='gray.200' // colorはChakraUIで定義されたものから
  color='blue.500'
  size='xl'
/>
```

参考： [https://chakra-ui.com/docs/feedback/spinner#spinner-with-empty-area-color](https://chakra-ui.com/docs/feedback/spinner#spinner-with-empty-area-color)

そのままでも機能としては十分ですし、スタイルも上書きして使用できるので汎用的です。


また、その他よく使うモジュールもいくつか紹介します。

### Box
`Box`モジュールは、デフォルトでは `div`としてレンダリングされるもので、抽象度が高いのでwrapperなどに使用することが多いです。

以下の場合は、Labelコンポーネント（タグのようなUI）を作成した例です。

```tsx
// Boxモジュールをインポート
import { Box } from '@chakra-ui/react'

// props 型定義
type LabelTagProps = {
  children: string
}

export const Label = ({ children }: LabelTagProps) => {
  return (
    <Box
      as={'span'} // spanとしてタグを出力
      bg={'white'}
      border={'1px solid'}
      borderColor={'Cyan.500'}
      borderRadius={'4px'}
      color={'Cyan.500'}
      fontSize={{ base: 'xs', md: 'sm' }} // オブジェクトで渡すことでレスポンシブ対応
      fontWeight={700}
    >
      {children}
    </Box>
  )
}
```

こちらでもインポートしたChakraUIの `Box`を、Reactコンポーネントと同じような形で適用します。

propsを渡すことで、スタイルを指定していきます。
ここで指定するスタイルの名前は、実際のCSSの指定とは違いキャメルケースとなるので注意が必要です。
とはいえ、chakraUIが気を利かせてサジェストを出してくれるので、困るケースはほとんどないかと思います。

また、`as`を指定することで、出力されるタグをオーバーライド可能です。
ここでは、`as`にspanを指定することで、spanタグとして使用しています。

> **The Box component is useful because it helps with 3 common use cases:**
>・ Create responsive layouts with ease.
>・ Provide a shorthand way to pass styles via props (`bg` instead of `backgroundColor`).
>・ Compose new component and allow for override using the `as` prop.
>
>**Boxコンポーネントは、次の3つの一般的なユースケースに役立つため便利です。**
>・レスポンシブレイアウトを簡単に作成できます。
>・propsを介してスタイルを渡すための簡単な方法を提供します（`backgroundColor`は`bg`となります）
>・新しいコンポーネントを作成し、`as`propsを使用してオーバーライドできるようにします。


（[公式ドキュメント](https://chakra-ui.com/docs/layout/box)より引用）

上記は、基本的にはどのモジュールでも同様な仕組みとなります。

例えば `DL/DT/DD`タグをマークアップをしたい場合、ChakraUIにはデフォルトでそのタグを出力するモジュールは用意されていません。

そのため、

```tsx
import { Box, Heading, Text } from '@chakra-ui/react'

...

<Box as={'dl'} mt={{ base: 4, md: 8 }}>
  <Heading as={'dt'}>
    タイトル
  </Heading>
  <Text as={'dd'}>
    内容
  </Text>
</Box>
```

このような形で `as`を利用すると「Heading」と「Text」という構造も直感的に読み取れるので、コードの可読性アップさせる書き方ができるケースもあります。

### Stack, VStack, HStack
このモジュールは、複数要素をラップすることで簡単に横並び・縦並びを実現でき、またそれら間のスペースをコントロールできます。
これはChakraUI独自の機能で、これを使うためにChakraUIを導入したいと言っても過言ではないほど便利な機能です。

- Stack：縦並び / 左寄せ
- VStack：縦並び / 横中央寄せ
- HStack：横並び / 縦中央寄せ

例えば `HStack`を使用することで、中の要素を横並びにし、`spacing`を指定することで隙間の大きさを調整できます。（デフォルトでも少し隙間が空いた状態になります）

![20220222-175224.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2385943/29ae0768-0f37-59db-37fa-0db25e85eebe.png)

```tsx
import { Box, HStack } from '@chakra-ui/react'

<HStack spacing='24px'> // 24pxの隙間を空ける
  <Box w='40px' h='40px' bg='yellow.200'>
    1
  </Box>
  <Box w='20px' h='20px' bg='tomato'>
    2
  </Box>
  <Box w='40px' h='40px' bg='pink.100'>
    3
  </Box>
</HStack>
```
また、propsで`spacing`を指定せずに、要素の間に`Spacer`というモジュールを使用することで、隙間を親の幅から自動で均等に配置してくれます。

![20220222-175549.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2385943/6bea6cad-1800-e4cd-1d7a-74486e66ff9f.png)

```tsx
import { Box, HStack, Spacer } from '@chakra-ui/react'

<HStack>
  <Box w='40px' h='40px' bg='yellow.200'>
    1
  </Box>
  <Spacer />
  <Box w='20px' h='20px' bg='tomato'>
    2
  </Box>
  <Spacer />
  <Box w='40px' h='40px' bg='pink.100'>
    3
  </Box>
</HStack>
```

上記のようなレイアウトは、`Flexbox`などでももちろん実現可能ですが、スタイルの指定をせずにタグから直感的にレイアウトがイメージできるのは大きなメリットであると思います。


### レスポンシブ対応
ChakraUIのレスポンシブ対応は、propsで指定するスタイルを **配列** もしくは **オブジェクト** で渡す形で実装します。

#### ブレイクポイントの指定
```tsx
import { createBreakpoints } from '@chakra-ui/theme-tools'

const breakpoints = createBreakpoints({
  sm: '30em',
  md: '48em',
  lg: '62em',
  xl: '80em',
  '2xl': '96em',
})
```
テーマファイルに、`createBreakpoints`を使ってブレークポイントを指定することができます。

#### レスポンシブスタイルの指定方法
##### ①配列形式

配列で指定する場合、小さい方のブレイクポイントから順に適用されます。
```tsx
<Box
  bg='teal.400'
  width={[
    '100%', // 0-30em
    '50%', // 30em-48em
    '25%', // 48em-62em
    '15%', // 62em+
  ]}
/>
```
この例だと4つの指定をしているため、62em以上は4つ目のスタイルが適用されます。

##### ②オブジェクト形式
オブジェクトで指定する場合は、ブレイクポイントのエイリアスを使用してレスポンシブ値を定義します。
```tsx
<Text fontSize={{ base: '24px', md: '40px', lg: '56px' }}>
  This is responsive text
</Text>
```
`base` は設定された値以下の場合を定義します。つまり、この例では

- base： 0-48em
- md： 48-62em
- lg： 62em+

という2つのブレイクポイントでスタイルが分かれるように記述されています。

実装した所感としては、配列よりもオブジェクトで定義したほうがブレイクポイントの範囲が明示的ですし、
柔軟に色々なパターンに対応できるため使いやすい印象です。

## ChakraUIのメリット・デメリット まとめ
### メリット
- デフォルトのデザインが割とキレイなのでそのまま使っても違和感がない
    - Figmaにテンプレートがあるのでデザイナーさんとも連携取りやすい
- Pure Typescript で開発者体験◎
- 便利な独自フックが存在する
- アクセシビリティもいい感じに対応されている

### デメリット
- 使えるモジュールの数が多いので、最初は学習コストがそれなりに掛かる
- 実装デザインが完全オリジナルな場合、ChakraUIのデフォルトのスタイルが邪魔に感じる時もある
    - 場合によっては、HeadlessUIとかで機能だけ使ってスタイルは自分で書く方式を選択肢に入れる
- Themeのカスタマイズ管理は初期実装にコストがかかる

## 最後に

ChakraUIの概要についてざっくりとご説明しました。
使用感やコードの書き味のイメージを掴んでいただけていれば幸いです。

また、今回詳しく記載しきれなかった以下のような便利機能がまだまだあります。

- ThemeでのUIカスタマイズ（スタイルをpropsで直接上書きしない方法）
- 独自フックについて
- アクセシビリティの考慮

こちらについても、機会があればまとめてみたいと思います！

## 参考サイト
- [TailwindCSS と比べつつ Chakra UI に入門する](https://qiita.com/JSON_HardCoder/items/4ac6314e940ec6d2ad1a)
- [Chakra UIの歩き方 & Tips集](https://zenn.dev/terrierscript/books/2021-05-chakra-ui)