Overview
Cerberus provides a way to write CSS-in-JS with better performance, developer experience, and composability. One of its key features is the ability to create multi-variant styles with a type-safe runtime API.
A recipe consists of these properties:
className: The className to attach to the componentbase: The base styles for the componentvariants: The different visual styles for the componentcompoundVariants: The different combinations of variants for the componentdefaultVariants: The default variant values for the component
Defining the recipe
Use the defineRecipe identity function to create a recipe.
import { defineRecipe } from '@pandacss/dev'
export const buttonRecipe = defineRecipe({
base: {
display: 'flex',
},
variants: {
visual: {
solid: { bg: 'red.200', color: 'white' },
outline: { borderWidth: '1px', borderColor: 'red.200' },
},
size: {
sm: { padding: '4', fontSize: '12px' },
lg: { padding: '8', fontSize: '24px' },
},
},
})Then add it to your theme:
import { createCerberusConfig, createCerberusPreset } from '@cerberus/panda-preset'
export default createCerberusConfig({
presets: [createCerberusPreset()],
include: ['./app/**/*.{ts,tsx}'],
exclude: [],
theme: {
extend: {
recipes: {
myButton: buttonRecipe,
},
},
},
})Using the recipe
After defining recipes, we recommend using the Panda CLI to generate theme typings for your recipe.
npm panda codegenpnpm panda codegendeno panda npm:codegenbun panda codegenThere are two ways to use the recipe in a component:
- Directly in the component
- Creating a component (recommended) with the
Cerberusfactory
With the Cerberus Factory
Import the button recipe from your local styled-system/recipes folder and use it within your component.
import { createCerberusPrimitive } from '@cerberus/react'
import { buttonRecipe, type ButtonVariantProps } from './button.recipe'
const { withRecipe } = createCerberusPrimitive<ButtonVariantProps>(buttonRecipe)
function MyCustomButton(props: ButtonVariantProps) {
const dynamicValue = props.something ? 'yes' : 'no'
return (
<button data-dynamic-thing={dynamicValue} {...props}>
Click Me
</button>
)
}
export const Button = withRecipe(MyCustomButton)Directly in the Component
splitVariantProps
You can also use the [recipe].splitVariantProps function to split the recipe
props from the component props. This is useful if you are not using the factory to create components.
'use client'
import { cerberus, type CerberusPrimitiveProps } from '@cerberus/react'
import { buttonRecipe, type ButtonVariantProps } from './button.recipe'
interface CustomButtonProps extends ButtonVariantProps {
doSomething: () => void
}
export function MyCustomButton(props: CerberusPrimitiveProps<CustomButtonProps>) {
const [recipeProps, restProps] = buttonRecipe.splitVariantProps(props)
const styles = buttonRecipe(recipeProps)
return (
<cerberus.button {...restProps} className={styles} onClick={restProps.doSomething}>
Click Me
</cerberus.button>
)
}TypeScript
To infer the recipe variant prop types, use the *VariantProps type.
import { buttonRecipe, type ButtonVariantProps } from './button.recipe'
export interface ButtonProps extends ButtonVariantProps {}Default Variants
The defaultVariants property is used to set the default variant values for the
recipe. This is useful when you want to apply a variant by default.
import { defineRecipe } from '@pandacss/dev'
export const buttonRecipe = defineRecipe({
base: {
display: 'flex',
},
variants: {
visual: {
solid: { bg: 'red.200', color: 'white' },
outline: { borderWidth: '1px', borderColor: 'red.200' },
},
size: {
sm: { padding: '4', fontSize: '12px' },
lg: { padding: '8', fontSize: '24px' },
},
},
defaultVariants: {
visual: 'solid',
size: 'sm',
},
})Compound Variants
Use the compoundVariants property to define a set of variants that are applied
based on a combination of other variants.
import { defineRecipe } from '@pandacss/dev'
export const buttonRecipe = defineRecipe({
base: {
display: 'flex',
},
variants: {
visual: {
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',
visual: 'outline',
css: {
borderColor: 'blue.500',
},
},
],
})When you use the size="small" and visual="outline" variants together, the
compoundVariants will apply the css property to the component.
<Button size="small" visual="outline">
Click Me
</Button>Caveat
Due to the design constraints, using compoundVariants with responsive values
doesn't work.
This means a code like this will not work:
<Button size={{ base: 'sm', md: 'lg' }} visual="outline">
Click Me
</Button>For this cases, we recommend rendering multiple versions of the component with different breakpoints, then hide/show as needed.