레시피
레시피 (Recipes)
Chakra에서는 더 나은 성능, 개발 경험, 그리고 합성(composability)을 갖춘 CSS-in-JS 작성을 지원해요. 핵심 기능 중 하나는 타입 안전한 런타임 API로 다중 변형(multi-variant) 스타일을 만들 수 있다는 점이에요.
출처: 문서
본문
레시피는 다음 속성들로 구성돼요.
className: 컴포넌트에 붙일 classNamebase: 컴포넌트의 기본 스타일variants: 컴포넌트의 다양한 스타일 변형compoundVariants: 변형들의 다양한 조합defaultVariants: 컴포넌트의 기본 변형 값
레시피 정의하기
defineRecipe 항등 함수로 레시피를 만들어요.
import { defineRecipe } from "@chakra-ui/react"
export const buttonRecipe = defineRecipe({
base: {
display: "flex",
},
variants: {
variant: {
solid: { bg: "red.200", color: "white" },
outline: { borderWidth: "1px", borderColor: "red.200" },
},
size: {
sm: { padding: "4", fontSize: "12px" },
lg: { padding: "8", fontSize: "24px" },
},
},
})
레시피 사용하기
컴포넌트에서 레시피를 사용하는 방법은 두 가지가 있어요.
- 컴포넌트에서 직접
useRecipe로 사용하기 chakra팩토리로 컴포넌트를 만드는 방법 (권장)
RSC 팁: 내부적으로
useContext,useInsertionEffect같은 react 훅에 의존하므로"use client"지시어를 추가해야 해요.
컴포넌트에서 직접 사용하기
useRecipe 훅으로 컴포넌트의 레시피를 가져와요. 그런 다음 레시피에 변형 props를 호출해 스타일을 얻어요.
"use client"
import { chakra, useRecipe } from "@chakra-ui/react"
import { buttonRecipe } from "./button.recipe"
export const Button = (props) => {
const { variant, size, ...restProps } = props
const recipe = useRecipe({ recipe: buttonRecipe })
const styles = recipe({ variant, size })
return <chakra.button css={styles} {...restProps} />
}
splitVariantProps
variant와 size props를 props에서 구조 분해해 레시피에 전달한 걸 볼 수 있어요. 더 똑똑한 방법은 레시피 props를 컴포넌트 props에서 자동으로 분리하는 거예요.
recipe.splitVariantProps 함수로 레시피 props를 컴포넌트 props에서 분리할 수 있어요.
"use client"
import { chakra, useRecipe } from "@chakra-ui/react"
import { buttonRecipe } from "./button.recipe"
export const Button = (props) => {
const recipe = useRecipe({ recipe: buttonRecipe })
const [recipeProps, restProps] = recipe.splitVariantProps(props)
const styles = recipe(recipeProps)
// ...
}
TypeScript
레시피 변형 prop 타입을 추론하려면 RecipeVariantProps 타입 헬퍼를 사용해요.
import type { RecipeVariantProps } from "@chakra-ui/react"
import { buttonRecipe } from "./button.recipe"
type ButtonVariantProps = RecipeVariantProps<typeof buttonRecipe>
export interface ButtonProps extends React.PropsWithChildren<ButtonVariantProps> {}
컴포넌트 만들기
chakra 함수로 레시피에서 컴포넌트를 만들어요.
Note: 레시피를
chakra함수에 인라인할 수도 있어요.
"use client"
import { chakra } from "@chakra-ui/react"
import { buttonRecipe } from "./button.recipe"
export const Button = chakra("button", buttonRecipe)
이제 컴포넌트를 사용하면서 레시피 속성을 전달하면 돼요.
import { Button } from "./button"
const App = () => {
return (
<Button variant="solid" size="lg">
Click Me
</Button>
)
}
기본 변형 (Default Variants)
defaultVariants 속성은 레시피의 기본 변형 값을 설정할 때 사용해요. 변형을 기본으로 적용하고 싶을 때 유용해요.
"use client"
import { chakra } from "@chakra-ui/react"
const Button = chakra("button", {
base: {
display: "flex",
},
variants: {
variant: {
solid: { bg: "red.200", color: "white" },
outline: { borderWidth: "1px", borderColor: "red.200" },
},
size: {
sm: { padding: "4", fontSize: "12px" },
lg: { padding: "8", fontSize: "24px" },
},
},
defaultVariants: {
variant: "solid",
size: "lg",
},
})
복합 변형 (Compound Variants)
compoundVariants 속성으로 다른 변형들의 조합에 기반해 적용되는 변형 집합을 정의할 수 있어요.
"use client"
import { chakra } from "@chakra-ui/react"
const button = cva({
base: {
display: "flex",
},
variants: {
variant: {
solid: { bg: "red.200", color: "white" },
outline: { borderWidth: "1px", borderColor: "red.200" },
},
size: {
sm: { padding: "4", fontSize: "12px" },
lg: { padding: "8", fontSize: "24px" },
},
},
compoundVariants: [
{
size: "small",
variant: "outline",
css: {
borderWidth: "2px",
},
},
],
})
size="small"과 variant="outline" 변형을 함께 사용하면 compoundVariants가 해당 css 속성을 컴포넌트에 적용해요.
<Button size="small" variant="outline">
Click Me
</Button>
주의사항
디자인 제약 때문에 compoundVariants와 반응형 값을 함께 사용할 수는 없어요.
즉, 다음과 같은 코드는 동작하지 않아요.
<Button size={{ base: "sm", md: "lg" }} variant="outline">
Click Me
</Button>
이런 경우에는 브레이크포인트별로 여러 버전의 컴포넌트를 렌더링한 뒤, 필요에 따라 보이기/숨기기를 하는 걸 권장해요.
시맨틱 토큰 사용하기
테마의 시맨틱 토큰을 레시피에서 사용할 수 있어요. 이름으로 참조하면 돼요.
import { defineRecipe } from "@chakra-ui/react"
export const buttonRecipe = defineRecipe({
base: {
bg: "bg.muted", // semantic token
color: "fg", // semantic token
borderRadius: "l2", // semantic radius
},
variants: {
variant: {
primary: {
bg: "colorPalette.solid", // virtual color
color: "colorPalette.contrast",
},
},
},
})
흔히 쓰는 토큰: bg, fg, border, colorPalette.*
테마에서 사용하기
레시피를 재사용 가능한 형태로 쓰려면 시스템 테마로 옮기고 theme.recipes 속성에 추가해요.
import { createSystem, defaultConfig, defineConfig } from "@chakra-ui/react"
import { buttonRecipe } from "./button.recipe"
const config = defineConfig({
theme: {
recipes: {
button: buttonRecipe,
},
},
})
export default createSystem(defaultConfig, config)
TypeScript
CLI를 사용해 레시피용 타입을 생성한 뒤 컴포넌트에서 import해요. postinstall, CI, 모노레포에서 typegen을 실행하는 방법은 CLI 문서를 참고하세요.
npx @chakra-ui/cli typegen ./theme.ts
그런 다음 생성된 타입을 컴포넌트에서 import해요.
import type { RecipeVariantProps } from "@chakra-ui/react"
import { buttonRecipe } from "./button.recipe"
type ButtonVariantProps = RecipeVariantProps<typeof buttonRecipe>
export interface ButtonProps extends React.PropsWithChildren<ButtonVariantProps> {}
코드 업데이트
레시피를 컴포넌트에서 직접 사용하고 있다면, useRecipe가 key 속성을 사용해 테마에서 레시피를 가져오도록 업데이트해요.
const Button = () => {
- const recipe = useRecipe({ recipe: buttonRecipe })
+ const recipe = useRecipe({ key: "button" })
// ...
}