Closed43

Gatsby & TypeScript の個人ブログを Tailwind から Chakra UI に入門しつつ移行したログ

個人的なブログ https://blog.hppd.dev のブランチを切って Chakra UI に入門しつつ、よさげだったら Tailwind CSS からの移行も視野に入れる よさげなので移行する。

Chakra UI is a simple, modular and accessible component library that gives you the building blocks you need to build your React applications.

Reactでアプリを作るのに便利なやつを提供するシンプルでモジュラーでアクセシブルなコンポーネントライブラリ。思いっきしブログなんかで使おうとしているが広義のアプリということにしてほしい。

ブログには大仰すぎると感じたらTailwindを使い続ければよい (改善の余地がありそうということは前々から思っている)。

https://chakra-ui.com/docs/getting-started

とりあえず適当に Get Started を押す。必要なパッケージを入れる。

npm i @chakra-ui/react @emotion/react @emotion/styled framer-motion

framer-motion はなんかアニメーションライブラリっぽい。

https://www.framer.com/motion/

そしてEmotionに依存している。もともとtwin.macroを使ってTailwindとEmotion併用してたが、Emotionのv11がいつの間にか出てるのでアップグレードしておく。

https://emotion.sh/docs/emotion-11

Emotion 11でパッケージが色々とリネームされている。

Most of this renaming can be done automatically via a codemod by running the @emotion/pkg-renaming rule from @emotion/eslint-plugin with --fix over your codebase.

@emotion/eslint-plugin を入れてパッケージリネーム用の設定を走らせればいいらしい。このeslint-plugin自体も eslint-plugin-emotion という名前から変更されている。

eslintrc.json にこれを追加して eslint --fix でOK。うっかり @emotion/core からインポートしたりしそうなので設定はしばらく残しておく。

  "plugins": [
    "@emotion"
  ],
  "rules": {
    "@emotion/pkg-renaming": "error"
  }

Chakra UI を使うにはアプリを <ChakraProvider></ChakraProvider> で囲む必要があるのだが、Gatsbyの場合はプラグインを入れれば全部やってくれるので丸投げすればおk。

@chakra-ui/gatsby-plugin を入れる。gatsby-plugin-chakra-uiというのもあるがこれは公式ではない。

そういえば gatsby-plugin-emotion が古いので v5 に上げておく。

思想でも読んどく。訳は超意訳。

https://chakra-ui.com/docs/principles

Chakra UI is established on principles that keep its components fairly consistent. Understanding these concepts will help you better contribute to Chakra UI.

Chakra 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デザインの問題にこたえられるシンプルでコンポーザブルなコンポーネントをデザインすることだ。その目的から外れないように行動指針のまとめを作って発展させてきた。

composableは「構成可能な」って訳されるけどまずその日本語の意味がよくわからん。どうでもいいけど「日本語は漢字があるから初見の単語の意味も字面から捉えられる」とか言ってる人間にこれを叩きつけたい。個人的にはパーツを組み合わせて大きなものを組み上げられるみたいな感じだと勝手に解釈してる。

  • Style Props: All component styles can be overriden or extended via style props to reduce the use of css prop or styled(). Compose new components from Box.

propsを使ってスタイルを上書きしたり拡張したりできるらしい。Box から新しいコンポーネントを構成するっていうのは進めないとよくわからん。

  • Simplicity: Strive to keep the component API fairly simple and show real world scenarios of using the component.

コンポーネントのAPIをシンプルに保ち、現実での使い方を示す。

  • Composition: Break down components into smaller parts with minimal props to keep complexity low, and compose them together. This will ensure that the styles and functionality are flexible and extensible.

/compos(e|able|ition)/ についての解説あるな。コンポーネントをより小さいパーツごとに分解してpropsを減らすことで複雑さを抑え、そんでそれらのパーツを組み合わせて使う。こうするとスタイルと機能の柔軟性と拡張性が上がる。

  • Accessibility: When creating a component, keep accessibility top of mind. This includes keyboard navigation, focus management, color contrast, voice over, and the correct aria-* attributes.

アクセシビリティ第一。キーボードナビゲーション、フォーカス管理、色のコントラスト、音声読み上げ、aria-*属性。

  • Dark Mode: Make components dark mode compatible. Use our ColorModeProvider component and useColorMode hook to handle styling. Learn more about dark mode.

コンポーネントはダークモードでも使える。カラーモード取るのに React Hooks が使える。

  • 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.

名前付けって作業が一番大変。一般的にpropの名前はそれが何をするのか明確になっている。真偽値なら does has is should みたいな助動詞を使う。Button には isDisabled isLoading みたいなのがある。

まあよくリーダブルコードとかで言われるのと同じ感じ。

タイムリーなことに UIT Meetup で Chakra UI のLTが行われている。観る

https://youtu.be/mbtDV5EkSAI?t=482
  • Pure TypeScript なので自然な補完が出る
  • デフォルトのBreakpointsがem指定
  • Stack Flexぽいやつ

そもそも今のデザインがあまり気に入ってないので、とりあえずブログのスタイル全部消すことにする。

Tailwind消しただけなのでEmotionの部分は普通に残ってる。

Container 読む。

https://chakra-ui.com/docs/layout/container

Containers are used to constrain a content's width to the current breakpoint, while keeping it fluid.

By default, it sets the max-width of the content to 60 characters (60ch) but you can customize this by passing custom maxWidth values or changing the container size tokens defined in theme.sizes.container.

Tailwindと挙動が違う?Tailwindの container は1個下のブレークポイントの幅になるのだが、ChakraUIのデフォルトだと 60ch 固定なのか?

そういえば gatsby-config.js 編集するの忘れてた。@chakra-ui/gatsby-plugin を追加してない。

そこそこ理解してきた。TailwindはVSCode拡張を入れないと補完が効かなくてつらいがChakra UIは普通に効いてくれるのでうれしい。

wrap="wrap-reverse" みたいな文字列もちゃんと文字列リテラルのUnionが取られている。TypeScriptわかってる。


おお…いい感じじゃん?

Stackdivider が地味に便利。並べるやつの隙間に線引こうとするとどうしても :first-childとかが必要になりがちだがこいつならまったく不要。最高。

コード
post-list.tsx
export const PostList: React.FC<PostListProps> = ({ nodes }) => (
  <VStack divider={<StackDivider />} spacing="1.5rem">
    {nodes.map(
      ({ excerpt, fields, frontmatter }) =>
        frontmatter?.tags &&
        fields?.path && (
          <PostLink
            path={fields.path}
            yyyymmdd={fields.yyyymmdd}
            title={frontmatter.title}
            tags={frontmatter.tags.filter(isntNull)}
            key={fields.path}
            excerpt={excerpt}
          />
        )
    )}
  </VStack>
);
post-link.tsx
export const PostLink: React.VFC<PostLinkProps> = ({
  excerpt,
  path,
  tags,
  title,
  yyyymmdd,
}) => (
  <Box as="article" px=".5rem">
    <Wrap spacing=".5rem" direction="row" wrap="wrap">
      {yyyymmdd && (
        <WrapItem>
          <PostDate path={yyyymmdd} />
        </WrapItem>
      )}
      <WrapItem>
        <TagList tags={tags} />
      </WrapItem>
    </Wrap>
    <Heading my=".5rem">
      <Hyper
        to={`/${path}`}
        css={css`
          font-feature-settings: "palt";
        `}
      >
        {title}
      </Hyper>
    </Heading>
    <Text color="gray.600">{excerpt}</Text>
  </Box>
);

既存の Box とかをコンポーネントにまとめたいとき、型を React.FC<React.ComponentProps<typeof Box>> とするのはあまりにも面倒だ。しかしそんな時のために Chakra UI にはくっそ便利な ChakraComponent<E, P> 型が用意されている。E'div' などの要素名を入れ、P にいつもどおりのPropsの型を入れるとそのとおりのPropsを受け取るコンポーネントの型になる。

うそかも。props の型が Box と合わない。

各種コンポーネントをChakraっぽいコンポジション的な感じに組み替えている。すごくわかりやすくなってるというか今までアンチパターン踏んでたっぽい?

リファクタリングを通してTailwind由来の className を消しまくれた。

サイドバーは LayoutSidebarComponent にわたす方式だったがサイドバーだけ独立させたらすごい楽になった。

要するに src/@chakra-ui/gatsby-plugin/theme.js でテーマオブジェクトを export default すればいいらしい.Gatsbyプラグイン系だと gatsby-config.js でこねこねさせるのが多いのでわりと珍しいと思った.珍しすぎて node_modules/@chakra-ui/gatsby-plugin/src/theme.js を直接上書きさせるのかと勘違いしてた.

こんな感じ.こういう奴は基本的にTypeScript入れると爆発して死ぬのでJavaScriptで書くが,extendTheme で引数に型がちゃんとついてるので補完は効く (エラーは出ない).

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

const theme = extendTheme({
  config: { initialColorMode: "dark" },
  colors: {
    gray: {
      50: "#F9F9FD",
      100: "#F0F1F8",
      200: "#E7E6F1",
      300: "#D2D3E3",
      400: "#ABAAC3",
      500: "#7F7C9A",
      600: "#55516A",
      700: "#37344A",
      800: "#211E2D",
    },
    purple: { 990: "#1a1723" },
  },
  components: {
    Link: {
      baseStyle: {
        color: "pink.400",
      },
    },
  },
});

export default theme;

感想,ChakraUIはたのしい.

  • 自然にTSXで型がつくのでたのしい
  • 普通にカスタマイズ性もそこそこあるのでたのしい
  • CSS modulesに戻すときとかつらそう 知らんけど
  • 使いやすいコンポーネント設計の参考になった
このスクラップは2020/12/30にクローズされました
ログインするとコメントできます