- react 向けの ui component library で ts ベース
- tailwind css っぽい utility first な設計だが default component も結構ある
- Emotion 依存で CSS のカスタマイズは CSS-in-JS でやるぽい
はじめに Chakra UIの歩き方 & Tips集 がよくまとまっているのでざっと読んで何ができるか、どういう設計思想か読んでおくとよき。
- chakra-templates.dev テンプレート集
- Error importing Framer Motion v5 in React (with create-react-app)
- webpack v4 だと 依存してる framer-motion の compile に失敗する
- ので v4.1.17 を入れろとのこと
- 【遊んでみた】framer-motion基本のき
- animation は framer/motion 依存 (公式: framer.com/motion)
- Transitions で簡単な animation 向けの hook も用意されている
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
画像ボタン的な。
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 }
}
/>
}
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>
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 をカスタマイズして 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>
)
}
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',
}
}
})
export const customizedTheme = extendTheme({
components: {
Container: {
baseStyle: {
// default 60ch とか
// ch 制御しづらいので px にしたほうがいい
maxW: '890px'
},
},
},
})
指定した 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 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' />
</>
}
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%)',
},
}
}
})
<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>
<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をどんどん使おう(リストレイアウト編)
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 は 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
みたいな「コンテンツの横幅統一させる」やつ。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>
<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>
すーどぅ。疑似要素も _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>
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>
</>
)
}
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>
- redux なんかで現在 index を操作したいときは
<Tabs index={} />
prop で制御 - focus 時に render したいときは
isLazy
が使えそう
- chakra-ui/chakra-ui#6213
- 複数 modal を一度に出すとき、最初に出したやつだけが body の overflow をいじるようにしないと内部ライブラリのエラーが出ちゃう
- 後続 modal で
blockScrollOnMount={false}
をつけてやればいいという話
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>