Recipes
Writing multi-variant styles with recipes in Cerberus.
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 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.
On this page