Skip to content

Instantly share code, notes, and snippets.

@yano3nora
Last active March 26, 2024 08:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yano3nora/3957714896143971b7638e78197cb696 to your computer and use it in GitHub Desktop.
Save yano3nora/3957714896143971b7638e78197cb696 to your computer and use it in GitHub Desktop.
[js: Chakra UI] TypeScript based component library for React. #js

Overview

chakra-ui.com
chakra-ui/chakra-ui - github.com

はじめに Chakra UIの歩き方 & Tips集 がよくまとまっているのでざっと読んで何ができるか、どういう設計思想か読んでおくとよき。

Refs

Getting Started

Chakra UI + Next.js - chakura-ui.com
TailwindCSS と比べつつ Chakra UI に入門する
Next.js (JavaScript/TypeScript) に Chakra UI を導入する方法

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

# アイコンセットも使うなら
# https://chakra-ui.com/docs/media-and-icons/icon#using-chakra-ui-icons
#
$ npm i @chakra-ui/icons
import { ChakraProvider } from '@chakra-ui/react'

function MyApp({ Component, pageProps }) {
  return (
    // Provider で囲んでやる
    <ChakraProvider>
      <Component {...pageProps} />
    </ChakraProvider>
  )
}
export default MyApp

Custom Component with ChakraProps

画像ボタン的な。

import React from 'react'
import { Button, ButtonProps } from '@chakra-ui/react'

/**
 * hover => click でふわっとサイズが変わる画像ボタン
 *
 * - bgImage, width, height 必須
 * - factory のままだと bgImage の伝播が行われない?
 *   っぽいので component で渡してる
 *
 * @example <ImageButton
 *            bgImage='/path/to/image.png'
 *            width='350px'
 *            height='100px'
 *          />
 */
export const ImageButton = (props: ButtonProps) => {
  const disabledProps = {
    bgColor: 'transparent',
  }
  const enableHoverProps = {
    bgColor: 'transparent',
    filter: 'brightness(105%)',
    transform: 'scale(1.03)',
  }
  const enabledActiveProps = {
    bgColor: 'transparent',
    transform: 'scale(0.98)',
  }
  const enabledFocusProps = {
    bgColor: 'transparent',
  }

  return <Button {...props}
    bgColor='transparent'
    bgSize='cover'
    _hover={props.disabled ? disabledProps : enableHoverProps}
    _active={props.disabled ? disabledProps : enabledActiveProps}
    _focus={props.disabled ? disabledProps : enabledFocusProps}
    style={
      // disabled 時の opacity のチラツキ防止
      // https://www.white-space.work/do-not-blur-hover-image-on-chrome/
      props.disabled
        ? { ...props.style, backfaceVisibility: 'hidden' }
        : { ...props.style }
    }
  />
}

Usage

import React from 'react'
import { Box } from '@chakra-ui/react'

export const Component = () => {
  return (
    // ほとんど `<div>` と同じレベルのプリミティブな component 。
    // https://chakra-ui.com/docs/layout/box
    //
    <Box>Hello</Box>
  )
}

カスタム component 作るならこんなか。

// https://zenn.dev/terrierscript/articles/2021-05-11-chakra-ui-component-extends

const MyButton = (props: ButtonProps) => <Button
  {...{
    width: 250,
    height: 50,
    rounded: 'full',
    colorScheme: 'red',
    ...props,
  }}
/>

return <MyButton
  variant={'outline'}
  onClick={() => console.log('clicked')}
>
  ボタンだよ
</MyButton>

Theme

Default Theme
Theme: サイズ・Spacingに関するTheme

default theme ざっと目を通しておくとよき。カスタムしたいなら ↓ あたりを参考に後述の extendTheme 使う感じかな。

// colors
<Box
  background={'blue.100'}
  borderColor={'teal.300'}
  color={'green.800'}
></Box>

// font-size
<Text fontSize='sm'>Small Text</Text>

// space (gap)
<Box padding={4} />  // 4px ではなく Theme の 4 = 1rem (16px)
<Box p={4} />        // p で padding の short hand
<Box mx={4} />       // こちらは margin short hand で x 横方向に 1 rem
  
// 4px にしたい場合は px を明示
<Box p={'4px'} />
  
// shadow を middle な大きさで頼む
<Box shadow='md' />

// 角丸 border-radis 頼む
<Box rounded='md' />

extendTheme

Theme Example

extendTheme で theme をカスタマイズして provider へ流し込める。

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

// デフォルトの breakpoints
// https://chakra-ui.com/docs/theming/theme#breakpoints
const breakpoints = createBreakpoints({
  sm: '30em',
  md: '48em',
  lg: '62em',
  xl: '80em',
  '2xl': '96em',
})

const theme = extendTheme({
  breakpoints,
  // デフォルトのフォント
  // https://chakra-ui.com/docs/theming/theme#typography
  fonts: {
    body: 'system-ui, sans-serif',
    heading: 'Georgia, serif',
    mono: 'Menlo, monospace',
  },
  // rem 基準をいじるならこんな
  // https://stackoverflow.com/questions/65270792/is-it-possible-modify-chakra-uis-rem-size
  styles: {
    global: {
      html: { fontSize: '24px' }
    }
  },
  // デフォルトのカラーモード
  // https://chakra-ui.com/docs/theming/theme#config
  config: {
    initialColorMode: 'light',
    useSystemColorMode: false,
  },
  sizes: {
    // container に maxW='container.sm' で渡すやつ
    container: {
      sm: '640px',
      md: '768px',
      lg: '1024px',
      xl: '1280px',
    },
  },
})

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

colorScheme のカスタマイズ

chakra-ui/chakra-ui#2846 (reply in thread)

https://palette.saas-ui.dev/ とか coolors.co みたいな chakra-ui 専用の generator 使ってカラーパレット作って ↓ のように登録しまくれとのこと。

const theme = extendTheme({
  colors: {
    lime: {
      // ぱっと動きみた感じだと 5, 6, 700 番台があれば
      // initial, focus, active, disabled 全部揃うぽい?
      // ... ので shade を 1 段階ずつ落としたやつを登録すればいいかな
      500: '#88d90d',
      600: '#69a905',
      700: '#4a7801',
    }
  }
})

Container の default サイズ変更

Container: Difference between maxW and maxWidth? #3626

export const customizedTheme = extendTheme({
  components: {
    Container: {
      baseStyle: {
        // default 60ch とか
        // ch 制御しづらいので px にしたほうがいい
        maxW: '890px'
      },
    },
  },
})

Responsive

Responsive Styles
レスポンシブにするなら複数の値を設定する

指定した sm とかの「この breakpoint 以上ならこの値」をレスポンシブに仕込める。

// 基本 24px
// ただし md (default 768px) 以上なら 40px 
// また lg (default 992px) 以上なら 56px 
//
<Text fontSize={{ base: '24px', md: '40px', lg: '56px' }}>
  This is responsive text
</Text>

Chakra Factory

Chakra Factory
どんなコンポーネントもChakra UIっぽく扱いたいならchakra factory機能を使う

import { Box, Tag, chakra } from '@chakra-ui/react'

// いまこの component でだけちょっとちいちゃい tag 作りたいんじゃあ
const STag = chakra(Tag, {
  baseStyle: {
    fontSize: 14,
    minHeight: '1.3rem',
  },
})

export const SomeComponent = () => {
  return <Box fontSize='sm'>
    ご利用は <STag>計画的に</STag> おねがいします。
  </Box>
}
// chakra っぽい props でカスタムできる plane な <button> タグ生成
<chakra.button
  px='3'
  py='2'
  bg='green.200'
  rounded='md'
  _hover={{ bg: 'green.300' }}
>
  Click me
</chakra.button>
import { chakra } from '@chakra-ui/react'
import Textarea from 'react-input-autoresize'

// ↑ 別ライブラリで生成される dom component を
// chakra で wrap して ... みたいなことできる
const AutoResizeInput = chakra(Textarea)

// もともと存在する <canvas> などの element を
// chakra factory で生成して埋め込むとかも可能
// <chakra.canvas> と同じ評価のはず
//
const Canvas = chakra('canvas')

<Canvas w={100} h={100} bg="red.100" />

function Example() {
  return <>
    <AutoResizeInput bg='red.200' fontSize='12px' />
    <Canvas w={100} h={100} bg='red.100' />
  </>
}

Type Infer

Chakra UIの再利用コンポーネント拡張の方法あれこれ

import {
  Button,
  ButtonProps,
  chakra,
  ChakraComponent,
  ComponentWithAs,
} from '@chakra-ui/react'

/**
 * 呼び出し側で Button の型補完が効かなくなる
 */
type ChakraCustomButton = ChakraComponent<
  ComponentWithAs<'button', ButtonProps>,
  ButtonProps // ので、こいつで最終的な型補完候補を明示
>

export const PrimaryButton: ChakraCustomButton = chakra(Button, {
  baseStyle: (props) => {
    // @ts-ignore
    const disabled: boolean = props.disabled || props.isDisabled

    return {
      color: 'white',
      bg: '#00a845',
      variant: 'solid',
      borderBottom: '6px solid #006937',
      fontSize: 35,
      px: 24,
      py: 6,
      _hover: disabled
        ? { bg: '#00a845 !important' }
        : {
            bg: '#00a845',
            filter: 'brightness(120%)',
          },
      _active: disabled
        ? {}
        : {
            transform: 'scale(0.99)',
            filter: 'grayscale(50%)',
          },
    }
  }
})

Inline span

consider changing Text to be inline #389

  • <span> 用の component が用意されてない
  • <Text> 配下に inline で追加するなら <Tag> <Link> とかしかできない
  • <Box> とか <Text> とか block 要素入れると怒られるので <chakra.span> で代用しろとのこと
  • あとは多分 <Text as={'span'} /> とかでもいいはず
import { chakra, Text } from '@chakra-ui/react'

<Text>
  John Doe
  <chakra.span color='gray.500'>
    Oct 23, 2021
  </chakra.span>
</Text>

Flex + Spacer

Flex

// よくあるヘッダの左側にサイト名、右側にボタンみたいな構成
<Flex>
  <Box p='2'>
    <Heading size='md'>Chakra App</Heading>
  </Box>
  <Spacer />  {/* ここに余白いっぱい広がる余白ができる */}
  <Box>
    <Button colorScheme='teal' mr='4'>
      Sign Up
    </Button>
    <Button colorScheme='teal'>Log in</Button>
  </Box>
</Flex>

Stack, HStack, VStack

Stackをどんどん使おう(リストレイアウト編)
VStack / HStackもどんどん使おう(コンポーネントレイアウト編)

  • gap が元からついてる <Flex> component 、構成のメインに使えそう
  • Stack, HStack, VStack の違い
    • Stack: 垂直のび (縦並べ flex) x 左寄せ
    • HStack: 水平のび (横並べ flex) x 中央寄せ
    • VStack: は垂直 (縦並べ flex) x 中央寄せ
  • 直下要素は React.Component じゃないといけないことに注意
// よくある avator icon つきのタイムライン表示
const CommentItem = () => {
  return <HStack>
    <Avatar />
    <Stack>
      <Heading size='sm'>
        Some member
      </Heading>
      <Box>
        loremipsum ...
      </Box>
    </Stack>
  </HStack>
}

Spacer

Using the Spacer
Spacerを使って楽にレイアウトしていく

Spacer は stretch な flex item なので、Flex でも Stack でも使える。

// space-between で書いたこいつと ...
<Flex justifyContent="space-between">
  <Flex>
    <Box>@taro:</Box>
    <Box>Hello!</Box>
  </Flex>
  <Flex>
    <Box>2021-05-01 12:00</Box>
  </Flex>
</Flex>

// こいつは等価になる
<Flex>
  <Box>@taro:</Box>
  <Box>Hello!</Box>
  <Spacer/>
  <Box>2021-05-01 12:00</Box>
</Flex>

// VStack と組み合わせるともっと読みやすい
<HStack spacing={1}>
  <Box>@taro:</Box>
  <Box>Hello!</Box>
  <Spacer/>
  <Box>2021-05-01 12:00</Box>
</HStack>

Container

Container

古の .container みたいな「コンテンツの横幅統一させる」やつ。VStack との組み合わせで画面構成するのが鉄板ぽい。

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

<VStack>
  <Container maxW='container.xl'>Extra-Large Container</Container>
  <Container maxW='container.lg'>Large Container</Container>
  <Container maxW='container.md'>Medium Container</Container>
  <Container maxW='container.sm'>Small Container</Container>
</VStack>

Button

Custom Button (as)

Custom Button

<Box
  as='button'
  pointerEvents='auto'
  _hover={{ opacity: '.9' }}
  _active={{ transform: 'scale(0.98)' }}
  _focus={{
    boxShadow:
      '0 0 1px 2px rgba(88, 144, 255, .75), 0 1px 1px rgba(0, 0, 0, .15)',
    }}
  onClick={() => {}}
>some button</Box>

Pseudo - hover, before, after ...

Pseudo

すーどぅ。疑似要素も _hover みたいな props 渡して使える、ごいすー。

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

// :hover style
<Button
  colorScheme="teal"
  _hover={{
    background: "white",
    color: "teal.500",
  }}
  _before={{
    content: '"🙂"', // "" で囲う必要がある
    display: 'inline-block',
    mr: '5px' ,
  }}
>
  Hover me
</Button>

Transitions

Transitions

useDisclosure

ScaleFade transition

function ScaleFadeEx() {
  const { isOpen, onToggle } = useDisclosure()

  return (
    <>
      <Button onClick={onToggle}>Click Me</Button>
      <ScaleFade initialScale={0.9} in={isOpen}>
        <Box
          p='40px'
          color='white'
          mt='4'
          bg='teal.500'
          rounded='md'
          shadow='md'
        >
          Fade
        </Box>
      </ScaleFade>
    </>
  )
}

with Redux

isOpen とか redux 依存にさせたいなら。

import { useSelector } from 'react-redux'

// dispatch => selector が true 返却で開くようにする
const isOpenModal = useSelector(selectIsOpenModal)
const { isOpen } = useDisclosure({
  isOpen: isOpenModal,
})

return <Slide direction='bottom' in={isOpen} style={{ zIndex: 10 }}>
  <Box
    p={10}
    mx={16}
    height={600}
    mb='80px'
    color='white'
    bg='teal.500'
    rounded='md'
    shadow='md'
    whiteSpace='initial'
  >
    Sit nulla est ex deserunt exercitation anim occaecat. Nostrud ullamco deserunt aute id consequat veniam incididunt duis in sint irure nisi. Mollit officia cillum Lorem ullamco minim nostrud elit officia tempor esse quis.
  </Box>
</Slide>

Tabs

Tabs Controlled Tabs

  • redux なんかで現在 index を操作したいときは <Tabs index={} /> prop で制御
  • focus 時に render したいときは isLazy が使えそう

Modal

  • chakra-ui/chakra-ui#6213
    • 複数 modal を一度に出すとき、最初に出したやつだけが body の overflow をいじるようにしないと内部ライブラリのエラーが出ちゃう
    • 後続 modal で blockScrollOnMount={false} をつけてやればいいという話

Table

Sticky

https://dev.classmethod.jp/articles/chakra-ui-table-with-sticky-header/

<Box width={'100%'} height={430} overflowY={'scroll'}>
  <TableContainer overflowX={'unset'} overflowY={'unset'}>
    <Table
      variant={'striped'}
      colorScheme={'teal'}
      size={'sm'}
      // 折り返し指定がないと、中の文字列によって横幅がはみでる
      wordBreak={'break-all'}
      whiteSpace={'normal'}
    >
      <Thead
        position={'sticky'}
        zIndex={'docked'}
        top={0}
        outline={'2px solid white'}
        bg={'gray.500'}
      >
        <Tr>
          <Th>hoge</Th>
          <Th>fuga</Th>
          <Th>piyo</Th>
        </Tr>
      </Thead>
      <Tbody>
        <Tr>
          <Td>
            hogehoge
          </Td>
          <Td>
            fugafuga
          </Td>
          <Td>
            piyopiyo
          </Td>
        </Tr>
      </Tbody>
    </Table>
  </TableContainer>
</Box>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment